Parsing in templates

In the previous post, we’ve starting looking at templates and at the DataView control. If you’ve been following along, you’ve probably noticed that we didn’t use system attributes (ie, sys: attributes) to specify the bindings. We could have use it, but we didn’t (btw, there’s a special sys:innertext attribute which you can use to set the innerText property of any DOM element). Why?

It’s simple: unlike other DOM objects, where markup extensions will only be searched for in system attributes, with templates everything is searched (including text nodes). in case you’re wondering why the system attribute restriction is used outside templates, just think in the time it would be needed for parsing all the nodes of a medium HTML document…yes, it would completely degrade the loading time of the page.

Now that you understand what’s going on with markup extensions, we can proceed and take a look at the real  process associated with template parsing. As I’ve said before, template parsing results in the creation of an of object of type Sys.UI.Template. After getting a Sys.UI.Template instance,you can make it “generate HTML” by calling the instantiateIn method.I like to call this template instantiation. Currently,the instantiateIn method has the following signature:

function Sys$UI$Template$instantiateIn(containerElement, 
data,
dataItem,
dataIndex,
nodeToInsertTemplateBefore,
parentContext)

As you can see, it expects several parameters:

  • containerElement: DOM element to which the template will add its generated HTML;
  • data: array with all the items that will be rendered. Notice that this is a complete list of elements to be rendered (for instance, in the previous example, the array passed to the data property will be passed in);
  • dataItem: reference to the current element obtained from the data source (ie, from the array specified in the data property) which can be used to influence the generated HTML (it all depends on the use of bindings);
  • dataIndex: integer which identifies the position of the dataItem in the data array object;
  • nodeToInsertTemplateBefore:reference to HTML node used as placeholder for the instantiation of the template (used when the generated HTML nodes shouldn’t be rendered as a direct children of the container – more about this in future posts);
  • parentContext: Sys.UI.TemplateContext object. This object has several interesting properties, but we’ll  leave it for a future post.

Now, if like me, you’re a curious guy, you’ve probably went straight into the JS file and looked for the instantiateIn method definition:

function Sys$UI$Template$instantiateIn(containerElement, 
data, dataItem, dataIndex,
nodeToInsertTemplateBefore, parentContext) {
containerElement = Sys.UI.DomElement.resolveElement(containerElement); nodeToInsertTemplateBefore =
(nodeToInsertTemplateBefore ?
Sys.UI.DomElement.resolveElement(nodeToInsertTemplateBefore)
: null); this._ensureCompiled(); return this._instantiateIn(containerElement, data,
dataItem, dataIndex, nodeToInsertTemplateBefore,
parentContext, this._instanceId++); }

Most work is delegated to the_instantiateIn method. Searching for this method in the source files won’t take you far (_instantiateIn is a property which initialized to null). There’s also an _ensureCompiled method call which looks promising. In fact, it’s there that relies the solution to our quest.

That method ends up calling the recompile method, which is responsible for parsing the HTML and building a JavaScript function which is used to initialize the _instantiateIn property of the template object. The next snippet shows the function that results from the parsing of the template presented in the previous post (I’ve rearranged the text for making it easier to read, making it incorrect – notice that the variable declaration should be in a single line):

function anonymous(
__containerElement, __data, $dataItem, $index,
__referenceNode, __parentContext, __instanceId) { $index = (typeof ($index) === ''number'' ? $index : __instanceId); var $component, __componentIndex, __e, __f, __topElements = [],
__d = 0, __p = [__containerElement],
$element = __containerElement,
$context = new Sys.UI.TemplateContext(),
$id = function(prefix) { return $context.getInstanceId(prefix); }; $context.data = (typeof (__data) === ''undefined'' ? null : __data); $context.components = []; $context.nodes = __topElements; $context.dataItem = $dataItem; $context.index = $index; $context.parentContext = __parentContext; $context.containerElement = __containerElement; $context.template = this; Sys.UI.Template._contexts.push(__topElements); with ($context.keys) { with ($dataItem || {}) { __d++; $element = __p[__d] = document.createElement(''LI''); __topElements.push($element); __d++; __p[__d - 1].appendChild(document.createTextNode(name)); __p[__d - 1].appendChild(document.createTextNode(" - ")); __p[__d - 1].appendChild(document.createTextNode(address)); --__d; $element = __p[__d]; --__d; } } $context._onInstantiated(__referenceNode); return $context; }

As you can see, there are several things going on here. For starters, the method always returns an instance of type Sys.UI.Template with important info filled during the instantiation of the template (in future posts, we’ll see that this info is kept by the DataView control and that you can access it to get information about the items generated by that control).

Even though you’re not seeing it explicitly in the previous anonymous function, I can assure you that the $element which gets created ends up being added into the container (the _onInstantiated method ensures that the new element is inserted in the correct place).

You should also keep in mind that the function is (probably) the most simple case you’ll get. Code will be more complex when you start adding event handling or commands to templates.

If you’ve read the ASP.NET 4.0 white paper, you’ve probably found out that templates have the pseudo-column concept: you can use $index and $dataItem properties to get the current position of the item that is being rendered and the item itself. Now that you’ve seen what happen “behind the scenes”, you can easily understand why you’ve got access to those pseudo-columns (btw, I don’t like this name; I’d prefer pseudo-variables or pseudo-properties)…and you should also know which “special” variables you can use from within your binding expressions 🙂

And I guess this sums it up nicely. By now, you should have a good idea of what happens when you define a template and on why you can use special properties in your code. There’s still much more to discuss, but we’ll leave it for future posts. Stay tuned for more on MS AJAX.

~ by Luis Abreu on October 15, 2009.

Leave a comment