Search code examples
nunjucks

nunjucks loop variable filter


var dataArr = [{
    name: 'name',
    value: 'lcat'
}, {
    name: 'score'
    value: '123.852',
    filter: 'round'
},{
    name: 'groups'
    value: [1,2,3,6,0],
    filter: 'sort'
}]

{% for data in dataArr %}
    <div>{{ data[value] | data.filter }}<div>
{% endfor %}

The console error: Error: filter not found: data.filter.

How do I write the loop?


Solution

  • (1) You can use if to branch the loop based on a condition observed in the dataset. For example this Nunjucks code:

    {% for data in dataArr %}
        {% if data.filter == 'round' %}
            <div>{{ data.value | round }}</div>
        {% elif data.filter == 'sort' %}
            <div>{{ data.value | sort }}</div>
        {% else %}
            <div>{{ data.value }}</div>
        {% endif %}
    {% endfor %}
    

    produces this html code:

    <div>lcat</div> <div>124</div> <div>0,1,2,3,6</div>
    

    From your dataset.

    You can play with it in this jsFiddle: http://jsfiddle.net/xmojmr/gbLLryuz/


    (2) Or you can roll your own filter which will take the arbitrary filter expression string defined in the dataset and inject the code (unprotected) into the page building engine (You can read little bit about why intentionally allowing code injection is not good idea at Wikipedia: Code injection)

    For example if env variable is of type Nunjucks.Environment then adding following filter before you run the JavaScript template rendering code

    env.addFilter('eval', function(value, filterExpression) {
        return env.renderString(
            "{{ filterArgument | " + filterExpression + " }}",
            { filterArgument: value }
        );
    });
    

    enables the use of simplified equivalent Nunjucks code

    {% for data in dataArr %}
        {% if data.filter %}
            <div>{{ data.value | eval(data.filter) }}</div>
        {% else %}
            <div>{{ data.value }}</div>
        {% endif %}
    {% endfor %}
    

    The above code produces following html code:

    <div>lcat</div> <div>124</div> <div>123.85</div> <div>0,1,2,3,6</div>
    

    when applied to the dataset below (notice the new round(2)):

    {
        name: 'name',
        value: 'lcat'
    }, {
        name: 'score',
        value: '123.852',
        filter: 'round'
    }, {
        name: 'score2',
        value: '123.852',
        filter: 'round(2)'
    }, {
        name: 'groups',
        value: [1,2,3,6,0],
        filter: 'sort'
    }
    

    You can play with it in this jsFiddle: http://jsfiddle.net/xmojmr/jkb7ry9x/1/


    The way (1) is safe and reasonably fast but it assumes that you know the list of allowed custom filters ahead

    The way (2) is wild, unsafe and slower but enables using of any user-provided filter expression