custom variables in FOR with JsRender

2019-09-09 22:23发布

问题:

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?

回答1:

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}}