For me, there was an evolutionary process before getting to the point where I realized client side templating was important. It all started out in the olden days of using ASP.NET Web Controls. With Web Controls, the idea was pretty simple: You bind your control to a datasource, and any events you wanted to take action on in that control would require a postback to the server. These postbacks were hugely interruptive, especially when performing lots of different manipulations on a single page. So when “Atlas” and the UpdatePanel came along, it looked to be exactly what we needed. We build pages in the exact same way we always did, slap an UpdatePanel around the whole thing, and magically all our problems would go away. Well as we’ve all found out by now, it’s not that simple. The heaviness and waste of an entire page lifecycle on each async postback, combined with the limitations of the postback model itself made it useful only in certain situations (like removing the flicker from an existing website with heavy postbacks).
When I first started using jQuery, I quickly realized that I could circumvent the whole postback model in favor of ajax and REST services, but I still wasn’t ready to give up the WebControls like ListView and GridView that I’ve used for so long. That was when I had the idea of calling a service to render a UserControl on the server, and pass back the html to insert onto my page. After googling “render user control service”, I quickly found out that I wasn’t the only one thinking of this idea, and off I went to get it working.
After using it for a few scenarios, the drawbacks of this approach started to be more apparent. The first drawback was getting access to the actual data. Sure, you have rendered view, but what if you wanted to do more with a particular record in that view, like show it on a google map? Do you try and extract what you need from the rendered html, or do you have a separate ajax call to get just the data? Another challenging drawback was control state, like scroll location or pagination. For example, if you render a UserControl with a ListView and PagerControl on it, you get paging buttons that are absolutely useless. I got around this by using jQuery to intercept the click events of those page buttons to call the rendering service, but these issues had me feeling like I was hacking the solution just to get the control and accessibility I needed. Finally, there is the bloat factor. Yes, this solution is much much lighter than the UpdatePanel + postback model, but not nearly as light as just passing the data down as JSON and rendering that data on the client.
Which brings me to the second-to-last step in the journey to client templates: rendering the data yourself with jQuery. Rick Strahl has a good blog post of this technique, but I’ll give an example of my own. You simply start with a block of html on your page that will serve as your template, like the following markup:
After you pull your JSON data from the web service, you clone the html for each record and inject the appropriate data, adding event handlers as you go:
This approach has a lot of things going for it — bandwidth efficiency, lightweight processing, and templates that are understandable. However, once you’ve coded a couple of these scenarios, two things become obvious. The first is that you are writing a hell of a lot of repetitive code just to match the correct element in the template with its value. The second thing is that you have to write lots more code if you want to do any kind of synchronization between two views that share the same data. How can we solve these two problems?
Chain.js is a jQuery plugin that aims to solve the templating and data synchronization shortcomings that I mentioned above. My favorite demo synchronizes two lists together, which really shows the simplicity and power of Chain.js in both templating and synchronizing two views dynamically. You start with the following markup:
We have two lists here, each with a template called “item”. The aim of the demo is to populate the “persons” list with a dataset, and then link the “filtered” list to show the items that have been filtered out of the first list. Here is the code that makes it all happen:
In the above code, the items plugin is initialized on the persons list with some data, and then .chain() is called to automatically bind the data to the template inside of the persons list. The default Chain.js data binder looks for classNames that correspond to the property on the JSON element. Chain.js automatically looks for the first item inside of the parent element to use as the template, but you can configure precisely where your template is with the anchor option. Next, a handler is setup on input’s keyup event so that we can use filter function to filter the list of items on every keystroke. Finally, we take our second list and link it to the collection ‘hidden’ on our first list, and call chain to initiate the data binding.
You can see the final result on the Chain.js Demo Page.
Chain.js is a great concept and great code, and it works very well in these demos. But it is still a project in it’s infancy, and that is apparent when you try to scale up the amount of data you are binding to, and the amount of views that are linked to the data. The performance literally grinds to a halt, even on the fastest machines.
Solving the current limitations of Chain.js
When analyzing the source code for Chain.js, I realized I could do something fairly simple right off the bat to speed up data-binding: use innerHTML instead of jQuery’s clone() method to create each templated item. The internals of the binding function “$update” in Chain.js look something like this (as of version 0.1):
Here is the modified code that uses innerHTML to create all the items ahead of time (without jQuery’s clone), and the loop picks each item out of the list for data binding:
Just this change alone made data binding about twice as fast in my testing. But with large datasets, that wasn’t enough of an increase. So I dug some more and I identified another issue: when synchronizing a series of views, the collections that you subscribe to are filtered with jQuery. So therefore, your master view is always required to create a corresponding DOM element for each data item. This means that if you have a thousand data items, but you only want your views to render and show a subset of those thousand items, then you still have to create a master view, which in turn requires a DOM element for each data item. That is a ton of extra work! So I set out to modify the code so that a master object is created that doesn’t have any dom elements that correspond to the data. Then, all other views are a linked to this object. But without the DOM, how are we supposed to use jQuery to filter our collections? The answer is, we don’t.
Enter Taffy DB
Other Client Template Libraries
It is pretty obvious that many developers see client templating as important going forward. This is evident in the amount of Client Template Libraries that are cropping up. Just recently, Microsoft released a preview of ASP.NET AJAX 4.0, which includes client-side template rendering. This seems promising, but I haven’t had much time to play around with it. There’s also jTemplates, PURE, and LightningDOM, although they are more geared towards just client side templating, rather than trying to tackle synchronization (like Chain.js and ASP.NET AJAX 4.0). If I’ve missed any other libraries, please let me know!