Knockout with JSRender Example - II

In this blog, I will create another example of working with knockout and jsrender. This example will demonstrate how to go up and down the object tree and how to register and compile the jsrender templates for better performance and reuse.
I am assuming that you know the basics of ASP.NET MVC. My project has a home controller with an index view and some css, js and templates. Let's take a look at the cshtml first:
<h6>Simple JSRender + Knockout example</h6>
<button data-bind="click:showHideDetails, text:showHideText, css:{'btn btn-medium':true, 'btn-danger': showDetails}">
</button>
<br />
<br />
<div data-bind="html:opHtml">
</div>
<script src="../../Scripts/home.js" type="text/javascript"></script>
The html code is pretty basic. There is a databound button with its click, text and css properties bound to variables we will define in the view model. There is a div which will show the final output and a script tag associating it with home.js. Let's take a look at home.js now:
/// <reference path="knockout-2.0.0.js" />
/// <reference path="jsrender.utils.js" />

var my = my || {};

$(document).ready(function () {
    my.viewModel = function () {
        var data = {
            players: [{
                name: 'Sachin Tendulkar',
                game: 'Cricket',
                description: '<b>Master Blaster</b>',
                likes: ["cricket", "seafood", "cars"],
                address: {
                    city: 'Mumbai',
                    country: 'India'
                }
            },
        {
            name: 'Roger Federer',
            game: 'Tennis',
            description: '<b>Fed Ex</b>',
            likes: [],
            address: {
                city: 'Basel',
                country: 'Switzerland'
            }
        },
        {
            name: 'Michael Jordan',
            game: 'BasketBall',
            description: '<b>His Airness</b>',
            likes: ["BasketBall", "golf", "haynes"],
            address: {
                city: 'Chicago',
                country: 'USA'
            }
        }]
        };

        var showDetails = ko.observable(false);
        var opHtml = ko.observable();
        var showHideText = ko.computed(function () {
            return showDetails() ? 'Hide Details' : 'Show Details';
        });

        var showHideDetails = function () {
            if (showDetails()) {
                my.utils.renderTemplate({
                    name: 'summary',
                    data: data.players,
                    selector: opHtml
                });
            } else {
                my.utils.renderTemplate({
                    name: 'details',
                    data: data.players,
                    selector: opHtml
                });
            }
            showDetails(!showDetails());
        };

        return {
            data: data,
            showHideDetails: showHideDetails,
            opHtml: opHtml,
            showHideText: showHideText,
            showDetails: showDetails
        };
    } ();

    ko.applyBindings(my.viewModel);
});
The home.js defines a view model as we have seen in the past examples. When the button is clicked showHideDetails function gets called. Here, we are calling a utility method (code below) to render the template. The code for this method looks like this:
var my = my || {};
$(document).ready(function () {
    my.utils = function () {
        var getPath = function (name) {
            return "../Templates/_" + name + ".tmpl.html";
        };

        var renderTemplate = function (item) {
            var file = getPath(item.name);
            $.when($.get(file))
                .done(function (tmplData) {
                    $.templates({ tmpl: tmplData });
                    item.selector($.render.tmpl(item.data));
                });
        };

        return {
            getPath: getPath,
            renderTemplate: renderTemplate
        };
    } ();
});
In this function, we first obtain the path where the template file exists. Then we use the jquery's when-done async operation to get that file and once the file is obtained execute the code in done. In done function, we receive the contents of the file, so we first register and compile this content into a variable called 'tmpl'. This is now compiled and registered so jsrender engine will make 'tmpl' available in all the places its in context. Then we had passed a knockout observable as the selector so we assign the rendered value to this selector. Notice that we call the render method on the 'tmpl' variable we just created and provide it the data passed by the calling method.
The summary template looks like:
<li><span>{{:#index+1}} - {{:name}}</span></li>
and the details template looks like:
<li>
    <span>{{:#index+1}} - {{:name}}
    <div>plays {{:game}} and lives in 
        {{for address}}
        {{:city}}, {{:country}}
        {{/for}}                 
    </div>
    <div>Also known as {{:description}} or in html {{html:description}}</div>
    {{if likes.length>0}}
    <div><b>Interests:</b> 
        <ul>
            {{for likes ~pName=#data.name}}
            <li>{{:#parent.parent.data.name}} (accessed up the hierarchy) likes {{:#data}}</li>
            <li>{{:~pName}} (accessed down the hierarchy) likes {{:#data}}</li>
            {{/for}}
        </ul>                
    </div>
    {{else}}
    <div><b>No Interests Defined</b></div>            
    {{/if}}
</li>
<br/>
In the details template you can see that we can use the #parent to go up the hierarchy and ~ to declare a variable at the top level and access it later somewhere down the hierarchy.
The output looks like:



When you click the button -