Skip to content

Instantly share code, notes, and snippets.

@getify
Created September 11, 2012 06:21
Show Gist options
  • Save getify/3696453 to your computer and use it in GitHub Desktop.
Save getify/3696453 to your computer and use it in GitHub Desktop.
how does your templating approach handle this task?
{
"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
@simonblee
Copy link

You can't iterate over objects in Dust (without a helper) so you can just massage the data into arrays of objects like. If you use LinkedIn's fork then you can use their custom @if and @eq helpers (I won't to keep the rending fast). This is not exactly what you asked for but I'd also like to keep the template generic without 'high' and 'low' options and then only change the data. Then you can reuse it:

{
    "settings" : [
        {name : "foo", options : [{ val : "low", check : true}, { val : "high" }] },
        {name : "bar", options : [{ val : "low" }, { val : "high", check : true }] },
        {name : "baz", options : [{ val : "low", check : true }, { val : "high" }] }
    ]
}

Then use the following template (to keep it more generic, I'd use a partial for the {#settings} array in a real project):

<h1>Settings</h1>
{#settings}
        <h2>{name}</h2>
        {#options}<input type="radio" name="{name}" value="{val}" {?check}checked{/check}>{val}{/options}
{/settings}

@simonblee
Copy link

...next time I'll set up my own gist so I can edit my typos...

@Pointy
Copy link

Pointy commented Sep 12, 2012

Yes, @getify, in doT a loop looks like this:

{{~ collection :item :index }}
  ... template content ...
{{~}}

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.)

@getify
Copy link
Author

getify commented Sep 12, 2012

@simonblee

a few comments:

  1. 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.

  2. If the options array that you loop over had a property called name, 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?

@getify
Copy link
Author

getify commented Sep 12, 2012

@Pointy

So, for a nested loop, would I do this?

{{~ collection :item :index }}
  ... blah ...
  {{~ collection :item2 :index2 }}
     outer item: {{! item }}  and inner item: {{~ item2 }}
  {{~}}
  ... blah ...
{{~}}

@Pointy
Copy link

Pointy commented Sep 12, 2012

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 :-)

@getify
Copy link
Author

getify commented Sep 12, 2012

@Pointy what happens if you accidentally name the inner loop variables the same as the outer loop variables?

@Pointy
Copy link

Pointy commented Sep 12, 2012

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".

@pasaran
Copy link

pasaran commented Sep 13, 2012

@fearphage
Copy link

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

@fearphage
Copy link

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

@BorisMoore
Copy link

BorisMoore commented Sep 15, 2012

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

@getify
Copy link
Author

getify commented Sep 16, 2012

thanks @BorisMoore!

@patrick-steele-idem
Copy link

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

@patrick-steele-idem
Copy link

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

@getify
Copy link
Author

getify commented Oct 28, 2012

Thanks so much Patrick! Very much appreciate the input for Raptor. :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment