Search code examples
jqueryjquery-templatesjsrenderjsviews

custom variables in FOR with JsRender


I'm migrating from jQuery templates to JsRender and I don't know how to fully translate an {{each}} into a {{for}}

With jQuery templates I could do something like this:

{{each (i, val) object.items}}
    <span data-index="${i}">${val}</span>
{{/each}}

Where object.items is an array of values and I could define a custom index and item variables to show data (in this case i and val). But how do I do the same thing in JsRender?

{{for object.items}}
    <span data-index="{{:#index}}">{{:#data}}</span>
{{/for}}

I know index and data are there to show the same thing like jQuery templates, but how can I define custom variables? Is that even possible?

UPDATE: The reason for this is to provide some context for the variable that I'm working with. Let me explain with and example (jQuery tmpl)

{{each (r, row) object.rows}}
    {{each (c, col) object.cols}}
        //work with both index and item knowing which one is which
    {{/each}}
{{/each}}

Is this kind of syntax/logic possible with your engine?


Solution

  • You can create a custom variable for the data item (equivalent to val in the jQuery templates version), as follows:

    {{for object.items itemVar='~val'}}
        <span>{{:~val}}</span>
    {{/for}}
    

    There is currently no feature for a custom named index variable. (One could imagine adding support using the syntax {{for object.items itemVar='~val' indexVar='~i'}} - but that is not currently implemented).

    But if your reason for wanting to provide a custom variable for #index is in order to make it available to nested block scopes, you can define a custom variable for #index on the nested tag, as follows:

    {{for object.items itemVar='~val'}}
        {{someNestedTag ~i=#index}}
            <span data-index="{{:~i}}">{{:~val}}</span>
        {{/someNestedTag}}
    {{/for}}
    

    Now to take the specific case of:

    {{each (r, row) object.rows}}
        {{each (c, col) object.cols}}
            //work with both index and item knowing which one is which
        {{/each}}
    {{/each}}
    

    Here is one way to do that in JsRender/JsViews:

    {{for grid.rows ~grid=grid}}
      {{for ~grid.cols ~rowIndex=#index ~row=#data}}
        Row {{:~rowIndex}} {{:~row.rowProp}}
        Col: {{:#index}} {{:colProp}}
      {{/for}}
    {{/for}}
    

    If you want to use data-linking, with JsViews, you would instead write the above:

    {^{for grid.rows ~grid=grid}}
      {^{for ~grid.cols ^~rowIndex=#index ~row=#data}}
        Row {^{:~rowIndex}} {^{:~row.rowProp}}
        Col: {^{:#index}} {^{:colProp}}
      {{/for}}
    {{/for}}
    

    Note that in the JsViews version, because I want the ~rowIndex to update dynamically through data-binding when preceding rows are removed, I use the syntax ^~rowIndex - which is an opt-in to data-binding. ~rowIndex would render correctly initially but would not update when rows are removed...

    You can see it

    And out of interest, see also here for grid views using custom tags:

    Additional Note: In response to the comment below "why not continue with the {{for (index, item) object}} syntax?"

    In JsRender you can create custom tags very easily, and all tags share a common structure: http://www.jsviews.com/#tags {{myTag arg0 arg1 namedProp1=xxx namedProp2=yyy}} ... {{/myTag}} using named parameters (props) and unnamed parameters (args).

    So itemVar='~val' works for any tag even custom tags (not just {{for ...}} ), uses the standard syntax for a named parameter, and allows you to create a contextual template parameter (helper parameter) val corresponding to the data item of the template block (or blocks).

    Also in JsRender the (expression) syntax already has a meaning as an arg. For example, {{for (1 + 2)}}{{:}}{{/for}} will output 3!

    In jQuery Templates, there is no support for custom tags, and the (i, val) syntax is special-cased for the {{each}} tag.

    In the common-case scenario you don't need custom variable names and the jQuery Templates syntax:

    {{each (i, val) object.items}}
        <span data-index="${i}">${val}</span>
    {{/each}}
    

    is (I would say) slightly more complex than the JsRender one:

    {{for object.items}}
        <span data-index="{{:#index}}">{{:}}</span>
    {{/for}}