BlueLemonCode.Com

Yet Another Tech Blog

Revisiting JsRender and more: Part II

Background

If you do not know much about JsRender and If you have directly landed on this page then, I would strongly suggest you to read this first part of article series before reading ahead.

And if you know at least something about JsRender and its usage then you may want to read ahead and give me some comments :)

Article Body

In this part of JsRender article series, I will straight dive into more advanced functionality and usage of JsRender with some examples.

In the last part of this article series, we created a simple table using JsRender template and classic NorthWind database's Order table. In this part, we will continue using the same example and extend some more functionality using advanced features available in JsRender

JsRender Custom tags

The example in last part contains a html table as a template which is displayed on web page as below.

For displaying data in such a basic form it suffices the purpose. However, let us assume that we want to identify out more repeating customer and display CustomerId with maximum orders. This can be easily (and efficiently) done on server side. However, In some scenarios it would be more sensible to add the logic in client side code.

We can easily create a custom tag so that we can reuse it to display maximum occurrence of data for any given column.

To start with, let us add new blank Js file in the project and add following code to create custom JsRender tag.

$.views.tags({
    // Custom tag to identify value with most or least occurence in given column/array
    MostOrLess: function (array) {
        var retunrVal = "";
            var tempArr = new Array();
            var maxField = { name: '', count: 0 };
            for (var i = 0; i < array.length; i++) {
                if (tempArr[array[i][this.props.field.toString()].toString()] != null)
                    tempArr[array[i][this.props.field.toString()].toString()] += 1;
                else
                    tempArr[array[i][this.props.field.toString()].toString()] = 1;
            }
            for (var prop in tempArr) {
                if ((maxField.count <= tempArr[prop] && this.props.mode == 'most') || 
                (maxField.count >= tempArr[prop] && this.props.mode == 'least' || maxField.count == 0)) {
                    maxField.count = tempArr[prop];
                    maxField.name = prop;
                }
            }
            maxField.count = 0;
            return maxField.name;  //return tag value
    }
});

I have formatted the custom tag code in such a way that, it will identify the value with most or least occurrence in given array/column data. This way we can reuse the same tag even when we have to find the CustomerId who is having least number of orders in the table.

The code logic used in the "MostOrLeast" function above is independent of custom tag.

So, the basic syntax for creating JsRender custom tag is as below. Please note that, you can create as many tags in the same block.

    $.views.tags({
        MostOrLess: function (array) {
            var retunrVal = "";
        //some logic to generate the value
        return returnVal;  //return tag value
        },

        SomeOtherTag: function (array) {
            var retunrVal = "";
        //some logic to generate the value
        return returnVal;  //return tag value
        }
    });

Now, we need to make use of this custom tag in our html template.Go to template file that we created in the last part of this article here and add below html code at the top of template

  <div style="min-height:30px">
    Customer with maximum orders is :
     <b>{{MostOrLess OrderData field="CustomerID" mode="most"}}{{/MostOrLess}}</b>
    <br/>
    Customer with minimum orders is :
     <b>{{MostOrLess OrderData field="CustomerID" mode="least"}}{{/MostOrLess}}</b>
  </div>

The highlighted part in above code snippet indicates use of custom tag. It includes tag name followed by name of Javascript object which includes whole table data and then followed by two custom tag properties as "field" and "mode" and their values.

Look back at the custom tags Javascript code. It uses values provided by both these custom tag properties as

   this.props.field.toString()
   this.props.mode.toString()

Now, run the page to see the output of custom tag in action

The custom JsRender tag provides powerful way to induce custom JavaScript logic right into template instead of creating overlapping Javascript function to do DOM manipulation.

JsRender Converters

JsRender converter is another powerful feature which should be used in less complex scenarios wherein you simply convert the value for displaying based on actual value received by JSON object.

In fact in JsRender, converter, custom tag and helper function are so closely aligned that we can use one feature instead of another without much difference. However, each of these features are created for different purpose.

continuing with our original "Orders" table example, Let's say we have a requirement to highlight cells in ShipCountry column where country name is "Brazil".

To do this, we will first create a converter which receives string value as country name and simply compares name of country to hard coded string "Brazil" and then returns the color value.

Please note that, such a hard coding is not done in any standard application code. The hard coding done in below snippet is for example purpose only.

$.views.converters({
    getColorCode: function (val) {
    return (val == "Brazil" ? "Orange" : "");
    }
});

Now, go to template file and locate the last <td> inside <tbody> tag. Change the cell tag to include the converter function as this

<td bgcolor="{{getColorCode:ShipCountry}}"> {{>ShipCountry}}</td>

As it can be seen above, we have used the bgcolor property of <td> tag. However, we have assigned the value returned by getColorCode converter which takes Country name as a parameter value.

The output is changed is below after running the page

JsRender Helpers

Helper functions are another feature available in JsRender which can be used for multiple purpose, It is mostly used to do some kind of formatting on data. Although, Helpers can be used to include more common functionality. However, they should be used when use of converter and custom tag is not appropriate.

The "Orders" table has a field called "Frieght" which contains decimal value with 4 fractional places. Let's say, we want to round up the values in this column. This is neither simple replacement of values nor this scenarios make a candidate for a creating full fledged custom tag.

Hence, we will create a helper function here which does little formatting of data. Go to JavaScript file and use below code to create the helper function called "round"

$.views.helpers({
    round: function(val) {
        return Math.round(val);
    },
});

The function uses the Math.round function of JavaScript to round up the passed value.

Now, go back to template file and locate the <td> tag of field named "Frieght". Change the tag as below.

<td> {{:~round(Freight)}}</td>

The highlighted portion in above line shows how to make use of helper function.

JsRender Paths

The another most useful JsRender feature is traversing the object tree using context paths.

you can refer to parent object in current context using syntax as "#parent". This can be used iteratively to traverse to grand parent object as "#parent.parent".

However, to read data value in any object level, it is required to use "#data". So, to read value from parent object, this should be used

#parent.data.FieldName

Similarly, to read data value from current context

#data.FieldName

I do now want to deviate from the ongoing example of "Orders" table and the non nested nature of this data does not provide opportunity to display the use of JsRender paths. Hence, I am skipping the demo code for this.

What's more

Although we have covered most of major features of JsRender, there are some related interesting points as below

  • JsRender is not dependent on jQuery. That means, we can replace our own jQuery code from all example we have seen with pure JavaScript code and it will work. The JsRender library is not dependent on jQuery library
  • If the Json data contains some html tags which you want to display as it is (without encoding) then, you can use either of below JsRender syntax

{{>FieldName}}
{{html:FieldName}}

  • You can also use nested templates as below wherein the template name is simply passed to for tag

<script id="movieTemplate" type="text/x-jsrender">
    {{if Data.Condition === true}}
    {{for Data tmpl='#TemplateForCondition1'/}}
    {{else}}
    {{for Data tmpl='#TemplateForCondition2'/}}
    {{/if}}
</script>

JsRender also provides many other small but powerful features which I might have missed or skipped. But they are indeed very useful in some scenarios. 

Conclusion

JsRender is very good candidate to be considered as templating engine for any application. It proves to be one of the best in terms of performance. It is also loaded with array of features which is difficult to find in other templating solutions available around.

Although JsRender is still in beta (at the time of this writing) but it promises soon to be in version 1 release as official jQuery plugin.

If I missed something to mention around jQuery then please add a line in comment. (I intentionally missed JsViews, since it is separate point of discussion)

Hope you enjoyed reading this post. Thanks for visiting!