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?
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
- here: http://jsfiddle.net/BorisMoore/e3ah577p/
- and a similar case for two-dimensional arrays: http://jsfiddle.net/BorisMoore/cGZZP/
And out of interest, see also here for grid views using custom tags:
- http://jsfiddle.net/BorisMoore/dg7x8mrc/
- http://jsfiddle.net/BorisMoore/uu7opr3z/
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}}