Column Templates for the jQuery Grids

Damyan Petev / Tuesday, April 17, 2012

In my last blog I mentioned that our jQuery Grids are getting a new column template option that would also be using our new high performance Templating engine and in this blog will try to expand on both templating and what can you do with it in the grids. And, yes, I keep mentioning grids because this will seamlessly work with both the flat data Grid and the Hierarchical Grid(as it would create instances of the flat grid to build its nested layouts) - you will soon be able to apply templates on every column.

Now as mentioned before, the Templating Engine functionality is integrated with multiple controls,among which are the grids. That translates in a few benefits – you get igTemplating scripts the moment you load the grid and the grid internally will call the templating function and provide data and so on. All you have to worry about is providing the template string. So the way templates work with the controls remains pretty much the same and you might not feel the change at all. When you do, though, you can start use the new column template functionality – similar to the existing row template, but rather than making a template for all the columns in the row, you can now create a template for just that one column you are interested in! That should be a step forward in terms of flexibility and getting what you need done fast.

Just a few steps

Adding in scripts

Now that is yet another subject we’ve been sharing bits here and there – for 12.1 we have a loader control that is there to handle scripts and styles for you (you can check out this blog post on using the jQuery Loader to manage resources). The best part is that it follows the dependencies of the control you need and loads them too. That should explain why this:

  1. $.ig.loader({
  2.     scriptPath: "../../Scripts/js/",
  3.     cssPath: "../../Content/css/",
  4.     resources: "igGrid.Paging"
  5. });

would result in loading those files:

Infragistics jQuery Loader managing dependencies - Templating as integral part of the grids

Notice the templating in there? Of course the same result would be observed if you use Hierarchical Grid instead or the ASP.NET MVC wrapper:

  1. @( Html.Infragistics().Loader()
  2.       .CssPath("../../Content/css/")
  3.       .ScriptPath("../../Scripts/js/")
  4.       .Render())
  5. @( Html.Infragistics()
  6.     .Grid(Model) //continues...

Template strings

As mentioned above, functionality remains the same – the grid will handle templating internally and you provide the sting only. For this example let’s assume you are to display Customers (and optionally Orders) from the Northwind database and you already have a controller action exposing it. That means that in your templates you can use the data fields by name (just keep in mind it is case sensitive). Templates can be provided within or separately from the grid definition if you want to keep it neat. Let’s turn the ‘Country’ field into a flag rather than simple text:

  1. var CountryTemplate = "<img alt=\"${Country}\" src=\"http://upload.wikimedia.org/wikipedia/commons/thumb/b/ba/Flag_of_${Country}.svg/167px-Flag_of_${Country}.svg.png\" ></img>";

A simple <img> tag would do the trick and the source images are actually the thumbnails from Wikimedia’s Sovereign-state flags page. While this would work for most, there are some exceptions (like UK and USA as seen in Northwind, rather than the full name used for the images) and the results are not quite there yet:

Infragistics jQuery Grid with image Column Template

To improve on that we would use some conditional blocks to make our template flexible. The engine provides support for logic like the following:

{{if <condition1>}} <template1> {{elseif <condition2>}} <template2> {{else}} <default template> {{/if}}

And here’s the full template once we apply that:

  1. var CountryTemplate = "{{if ${Country} == \'UK\'}} <img alt=\"${Country}\" src=\"http://upload.wikimedia.org/wikipedia/commons/thumb/a/ae/Flag_of_the_United_Kingdom.svg/200px-Flag_of_the_United_Kingdom.svg.png\""
  2.  + "{{elseif ${Country} == \'USA\'}} <img alt=\"${Country}\" src=\"http://upload.wikimedia.org/wikipedia/commons/thumb/a/a4/Flag_of_the_United_States.svg/190px-Flag_of_the_United_States.svg.png\" ></img>"
  3.  + "{{else}} <img alt=\"${Country}\" src=\"http://upload.wikimedia.org/wikipedia/commons/thumb/b/ba/Flag_of_${Country}.svg/167px-Flag_of_${Country}.svg.png\" ></img> {{/if}}";

And while still on the subject of conditional templates let’s do little trick for the column showing the customer ID. For the sake of the example lets have a special treatment for countries begging with ‘U’. How would we do that? The trick is that when you use conditional blocks, what is inside that block will be compiled as JavaScript code, unlike simple substitution that will only produce a string for a performance boost. That means you can call various functions and use JavaScript logic there:

  1. var CustomerTemplate = '# Invisible comment text # {{if ${Country}.lastIndexOf("U", 0) === 0 }}<p style=\'color:rgb(0, 175, 235);\'>Conpany: ${CompanyName} <p> {{else}}Customer ID: ${CustomerID} {{/if}}';

Note the usage of ‘lastIndexOf’ method, which would compare the index of the last discovered ‘U’ char while checking only the first position in the country and in the same way this can be a custom function to perform more complicated checks. Results from the above improvements look like so:

Infragistics jQuery Grid with conditional and multi-conditional Column Templates

Note the middle row displays the company name instead the the ID and the flag is now with proper source.

Besides complicated conditional logic you can also use ‘each’ block to produce the kind of template you need. Examples for that would be creating HTML list to be transformed into a tree control or select element. In both cases the loop would be required to go through all the internal items:

  1. var OrdersTemplate = "<select id=\"IdSelect\">{{each ${Orders} }}  <option>${Orders.OrderID}</option>  {{/each}}</select>";

The default select will contain all the OrderID-s for the current record. You can drill down the hierarchy even though some of those fields might not be included in the layout(but are in the data) and you can assign various attributes to your template markup which can then be used as targets for scripts and styles. Using that technique can provide great versatility in regards to what kind of content you have in your grid (turning numbers into ratings, taking child data to create a chart, etc. ).

Setting up the grid

What is left is to add a grid to your page (flat data or a hierarchical) and use the new ‘template’ property available for column options (of course you can apply templates to the child layouts). In the definition you will see the templates defined above assigned to columns, a template applied to the child layout inline and the dataRendered event used to transform the select elements into igCombo controls:

  1. $("#grid").igHierarchicalGrid({
  2.     height: "400px",
  3.     width: "650px",
  4.     autoGenerateColumns: false,
  5.     autoGenerateLayouts: false,
  6.     initialDataBindDepth: 1,
  7.     primaryKey: "CustomerID",
  8.     defaultChildrenDataProperty: "Orders",
  9.     dataSource: "/Home/CustomersAndOrders",
  10.     columns: [
  11.                 { headerText: "Customer ID", key: "CustomerID", dataType: "string"},
  12.                 { headerText: "City", key: "City", dataType: "string" },
  13.                 { headerText: "Country", key: "Country", dataType: "string", template: CountryTemplate },
  14.                 { headerText: "Orders", key: "EntityState", template: OrdersTemplate} //this column is just used to host the selects
  15.         ],
  16.     columnLayouts: [{
  17.         key: "Orders",
  18.         childrenDataProperty: "Orders",
  19.         autoGenerateColumns: false,
  20.         autoGenerateLayouts: false,
  21.         primaryKey: "OrderID",
  22.         columns: [
  23.                 { headerText: "Order ID", key: "OrderID", dataType: "number" },
  24.                 { headerText: "Freight", key: "Freight", dataType: "number", template: "Freight: <b>${Freight}</b>" },
  25.                 { headerText: "Shipped Date", key: "ShippedDate", dataType: "date" }
  26.             ],
  27.         features: [
  28.                 { name: "GroupBy",
  29.                     tyoe: "local"
  30.                 }
  31.             ]
  32.     }],
  33.     features: [
  34.         { name: "Paging",
  35.             type: "local",
  36.             pageSize: 10,
  37.             showPageSizeDropDown: false
  38.         },
  39.         { name: "Sorting",
  40.             type: "local"
  41.         }
  42.         ],
  43.         dataRendered: function (evt, ui) {
  44.             $("select[id~=\"IdSelect\"]").igCombo({
  45.                 width: "100px",
  46.                 selectionChanged: function (evt, ui) {
  47.                     ui.owner.element.parent().append("Selected: " + ui.items[0].text);
  48.                    }
  49.              });
  50.              //clear selections
  51.              $("select[id~=\"IdSelect\"]").igCombo("selectedIndex", -1);
  52.     }
  53. });

While the logic provided to handle the selection changed on the combo is rather simplistic, it is there to mark the possibility. And the resulting grid from the definition above combined with the templates looks like this:

Infragistics jQuery Hierarchical Grid with multi-conditional Column Templates, templated combo control and child layout column.

Conclusion

The Templating Engine provides plenty of functionality and now with column templates you can apply that just where it’s needed with familiar experience. While the snippet above uses a hierarchical grid, it is just to show it is possible to template the child layouts and the flat data grid would be templated in very much the same manner. You’ve seen some ideas on how you can make use of such features, but really what you can do with that is down to your needs and creativity!

Update: Download the demo project - an ASP.NET MVC application used as base, with two views - one where the grid is defined using the helper and one with nothing but pure script. Keep in mind you will need at least a Trial version of our jQuery controls (it's free!).