Oct 17

The DataView control: going imperative, take II

Posted in Uncategorized      Comments Off on The DataView control: going imperative, take II

A few posts back, we started talking about how we can use the DataView control in an imperative way. At the time, we were still working with preview 5. Now that preview 6 is out, we’ll keep discussing this topic, but we’ll be upgrading our code from preview5 to preview6.

As you probably recall, we’ve already removed the declarative instructions from the DataView control; the only thing missing was the HTML code used by the template. In an older post, we’ve seen that template parsing results in transforming the HTML into a JavaScript function which is used for instantiating the nodes when someone calls instantiateIn over a template instance. Our first approach for creating a template through an imperative approach might rely on rewriting the  instantiateIn function or the (private) _instantiateIn method (which it built automatically during the parsing of templates defined through markup). This does, in fact work. However, it requires much work (much more than I want to have, believe me!)

Fortunately, there’s another approach (thanks once more go to Dave Reed for pointing me in the right direction): we can handle the itemRendered event of the DataView. We still haven’t looked at the events generated by the DataView control. For now, it’s sufficient to understand that the control will generate the itemRendered after each template instantiation (which is run for each item that comes from the data passed to it). Here’s an example which shows how to do everything from JavaScript code:

    <script src="Scripts/MicrosoftAjax/start.debug.js" 
script> </head> <body> <ul id="dv"> <li>Top item</li> <li id="putItHere"></li> <li>Bottom item</li> </ul> </body> <script type="text/javascript"> var data = [ { name: "luis",address: "funchal" },{ name: "paulo", address: "lisbon" }, { name: "rita", address: "oporto" } ]; Sys.require([Sys.components.dataView]); Sys.onReady(function() { var helper = document.createElement("UL"); helper.innerHTML = "<LI></LI>"; var template = new Sys.UI.Template(helper); var placeholder = $get("putItHere"); Sys.create.dataView(Sys.get("#dv"), { data: data, itemTemplate: template, itemPlaceholder: placeholder, itemRendered: function(sender, e) { //e represents the datacontext var data = e.dataItem; var node = e.nodes[0]; node.innerHTML = + "-" + data.address; } } ); }); </script>

Lets see if I can explain what’s going on here:

  • We’re relying on the script loader object’s ability to load scripts on demand. Notice that we only add the start script and then require the extra downloads by using the Sys.require method helper.
  • as you can see, we’ve removed all the extra markup we had. Since everything is done through JavaScript, we don’t even need to “pollute” the HTML with the XML namespaces;
  • we pass an anonymous function to the Sys.onReady event to ensure that the code will be run when all the JavaScript files have been downloaded;
  • creating a template through JavaScript is not complicated: as you probably recall from the initial code, we’re interested in rendering LI elements with some text content in them. We had several options here, but I’ve opted for creating a UL element and setting its HTML to an empty LI. This ensures that the template instantiation ends up generating LI elements (don’t forget that the top element isn’t create when a template is instantiated; it’s simply used as a container element and its internals define the code used for each rendered item). We could also have passed the empty UL element too. In that case, we’d need to create the complete HTML from the itemRendered function and add it to the nodes property (instead of getting a reference to nodes[0]);
  • we’re already using preview 6 and that means more helpers! The script loader object ends up adding several helper methods to the Sys.create object which can be used for creating components and behaviors previously introduced through a defineScript(s) method call. And that’s why we can create the DataView control by using the Sys.create.dataview method! Notice that this method expects only two parameters: a reference to the DOM element to which the AJAX control will be associated and a list of properties/events which will be initialized with the indicated values;
  • as I’ve said, the itemRendered event is fired after each item obtained from the data property is “rendered” into HTML through a template instantiation. The function which handles the event receives two parameters: a reference to the control that generated the event and a reference to the Sys.UI.TemplateContext obtained through the template instantiation (recall from our template parsing study that template instantiation ends up returning an object of this type). Sys.UI.TemplateContext objects are interesting and we’ll return to them in the next post. For now, you can see that they let us get the object that was used for instantiating the template (notice the dataItem property) and get the HTML nodes that resulted from the template instantiation (nodes property). In our previous case, template instantiation ends up producing a single node (LI) and we reuse that knowledge for setting its content.

And there you go: as you can see, you’re not forced to “pollute” your markup with templates since you can do everything from script. However, I believe that “HTML pollution” is a small price to pay here, especially when you’ve got nested templates (more on future posts): the simplicity and productivity boost you get from them is undeniable!

And that’s it for now. Stay tuned for more on MS AJAX.