-
-
Save getify/3696453 to your computer and use it in GitHub Desktop.
{ | |
"settings" : { | |
"foo" : "low", | |
"bar" : "high", | |
"baz" : "low" | |
} | |
} |
<!-- | |
NOTE: see this comment for clarification of why this | |
scenario is structured this way intentionally: | |
https://gist.github.com/3696453#gistcomment-570903 | |
Scenario: | |
Given the data structure above in "1.json", how would you | |
use a templating engine to generate this snippet of html | |
as a string? NOTE: the spirit of this question is to | |
generate a string of markup with a template engine, NOT | |
to do DOM manipulation. Imagine needing to generate this | |
kind of markup server-side to send over the wire. | |
--> | |
<h1>Settings</h1> | |
<h2>foo</h2> | |
<input type="radio" name="foo" value="low" checked> low | |
<input type="radio" name="foo" value="high"> high | |
<h2>bar</h2> | |
<input type="radio" name="bar" value="low"> low | |
<input type="radio" name="bar" value="high" checked> high | |
<h2>baz</h2> | |
<input type="radio" name="baz" value="low" checked> low | |
<input type="radio" name="baz" value="high"> high | |
here's how "grips" template engine would handle this scenario: | |
https://gist.github.com/3706912 |
a few comments:
-
Imagine if there were 50, 100, 1000 items in the list, and each had 10 possible values (imagine a big survey page grid of radio buttons, for instance). you're talking about creating a huge "sparse" data structure of a bunch of repeated values, so you can specify the "columns" over again for each "row". doesn't that seem crazy inefficient?
I understand you prefer the idea of being generic, but in some cases, like for a survey, there's a fixed set (or range) of values for the "columns", and it's the UI designer who controls that in the template, not the back-end data layer. Asking the UI designer to create a complex data transform, AND have a whole bunch of extra data in memory, seems unnecessary to me. This is one of those cases where I think the template engine should assist.
-
If the
options
array that you loop over had a property calledname
, would that shadow/override the outer loop's{name}
so that you couldn't easily reference it inside the inner loop? How does that sort of variable scoping complication get handled in Dust?
So, for a nested loop, would I do this?
{{~ collection :item :index }}
... blah ...
{{~ collection :item2 :index2 }}
outer item: {{! item }} and inner item: {{~ item2 }}
{{~}}
... blah ...
{{~}}
Yes, exactly. It seems very natural to me because I'm coming from a JSP background, and that's pretty much exactly how iterating looks in JSP (except of course it's 1000% uglier in JSP :-)
@Pointy what happens if you accidentally name the inner loop variables the same as the outer loop variables?
Then the template doesn't work. I try to avoid that :-) I have a hard time thinking of a time that happened to me in JSP (or JavaScript for that matter) any time recently (or not-so-recently even). Generally I use pretty good names (not huge javaProgrammerApproved names but good ones), so that I can stay sane, so unless I've got a list of things with the same sort of thing inside it's pretty easy to keep the names straight. Heck even with a nested loop for a table, I can at least use "row" and "col".
doT.js
{{##def.radiogroup:
<h2>{{! name}}</h2>
{{~['low', 'high'] :value}}
<input type="radio" name="{{! name }}" value="{{= value }}"{{? it.settings[name] == value }} checked="checked"{{?}}/>
{{~}}
#}}
{{##def.settings:
<h1>Settings</h1>
{{~Object.keys(it.settings) :name}}
{{#def.radiogroup}}
{{~}}
#}}
{{#def.settings}}
doT.js (cleaned up a bit)
{{##def.radiogroup:
<h2>{{= name}}</h2>
{{~['low', 'high'] :value}}
<input type="radio" name="{{= name }}" value="{{= value }}"{{? it.settings[name] == value }} checked="checked"{{?}}/>
{{~}}
#}}
{{##def.settings:
<h1>Settings</h1>
{{~Object.keys(it.settings) :name}}
{{#def.radiogroup}}
{{~}}
#}}
{{#def.settings}}
Here's one approach you can use with JsRender: https://gist.github.com/3730412
UPDATE: The gist has been deleted. It is very easy to do in JsRender using the {{props}} tag
thanks @BorisMoore!
As requested, here's the solution for Raptor Templates. The below template will produce the exact output (less extra whitespace):
<c:template xmlns:c="core" params="settings">
<h1>Settings</h1>
<c:for each="(name,value) in settings">
<h2>$name</h2>
<c:for each="option in ['low', 'high']">
<input type="radio"
name="$name"
value="$option"
checked="${value === option ? null : undefined}" />
$option
</c:for>
</c:for>
</c:template>
Admittedly, it is a little odd to use "null" as the value for attributes that do not have a value in the resulting HTML, but it works. However, I prefer to to output checked="checked" at the expense of a few extra bytes since it's cleaner in my implementation and it still produces valid HTML:
<c:template xmlns:c="core" params="settings">
<h1>Settings</h1>
<c:for each="(name,value) in settings">
<h2>$name</h2>
<c:for each="option in ['low', 'high']">
<input type="radio"
name="$name"
value="$option"
checked="{?value === option;checked}" />
$option
</c:for>
</c:for>
</c:template>
You can easily try out the templates online at http://raptorjs.org/raptor-templates/try-online/
Let me know what you think.
Thanks,
Patrick
For reference, I created a Gist that shows the input Raptor template, the generated HTML and the compiled template:
https://gist.github.com/3966647
--Patrick
Thanks so much Patrick! Very much appreciate the input for Raptor. :)
Yes, @getify, in doT a loop looks like this:
Inside the loop, "item" and "index" are bound to the array element and its index, respectively. (Loops like that only work on arrays, but that's not a problem because it's so easy to transform an object into an array of name/value pairs.)