Search code examples
jqueryjquery-templates

nested templates problem


I would like to use the jQuery Templates. On first sight the {{tmpl}} tag seems to be the way to go, but I need to do an extra step when templating and I can't figure out how to do this.

There is a datatype-definition connteced to the properties of a data-object. In the example below 'movie' is a text-field, 'released' a numeric field. Alle text-fields use a certain template, so do all numeric fields. Those nested templates know nothing about the context (here movies) they are used in. So the tag used there is just ${value} always. The "outer" template instead knows how a movie should look like. Its tags (movie and released) are replaced by the "inner" datatype-related templates. The extra step is to replace ${value} before the outer templating is done.

All I could come up with is this:

//the template used for all string-fields
var templForStrings = "<b>${value}</b>";
//the template used for all numeric fields
var templForNum = "<i>${value}</i>";

//data of a movie
var data = {};
data.movie = "Cowboys and Aliens"; //a string field
data.released = 2011; //a numeric field

//the template to show a movie
var templForMovies = "<div>{{html movie}} ({{html released}})</div>";

//normally a loop over the fields here
var field = {value: data.movie};
data.movie = $.tmpl(templForStrings, field).fullhtml();

field.value = data.released;
data.released = $.tmpl(templForNum, field).fullhtml();

//now the movie template
$.tmpl(templForMovies, data).appendTo("body");

Of course this scenario is simplified. The datatype-templates are a lot more complex than that. The data can be anything. Because of ${value} meaning two different things here, I don't see how to use nested templates as provided by the plugin. But I'm quite sure there is a more elegant and faster way than my code (which even needs the fullhtml-Plugin. It would really be nice to have a template like this

<div>${movie} (${released})</div>

as these are creatable by end-users too, and should be as simple as possible.


Solution

  • Perhaps you can use a Helper function to do this:

    html:

    <div id="container"></div>
    
    <script id="tmplObject" type="text/x-jquery-tmpl">
    {{each $data}}
        {{html Helper($item, $index)}}
    {{/each}}
    </script>
    
    <script id="tmplNumber" type="text/x-jquery-tmpl">
    number: <b>${Value}</b><br/>
    </script>
    
    <script id="tmplString" type="text/x-jquery-tmpl">
    string: <i>${Value}</i><br/>
    </script>
    

    script:

    <script type="text/javascript">
    
        Helper = function($item, $index) {
            var value = $item.data[$index];
            var type = (typeof value.Value).toLowerCase();
            var tmplName = "#tmplObject";
    
            if (type == 'number') {
                tmplName = "#tmplNumber";
            }
            else if (type == 'string') {
                tmplName = "#tmplString";
            }
    
            var $node = $('<div>').append($(tmplName).tmpl(value)).remove();
            return $node.html();
        };
    
    
        $(function() {
            var data = {
                Name: { Value: "John" },
                Age: { Value: 999 },
                Type: { Value: {
                    Id: { Value: 123 },
                    TypeName: { Value: 'Human' }
                }
                }
            };
    
            $('#tmplObject').tmpl(data).appendTo('#container');
        });
    </script>