Windows Store Apps Using HTML5 – Basic Data Template Functions (2 of 4)

In part one, nested data templates were introduced. Nested data templates provide a great way to enhance the flexibility of an app’s user interface. The issue with using nested templates is the simple fact that creating templates in HTML files can be a tedious process. Also, the more templates there are, the more HTML files are needed. As more templates are added, the markup of the app becomes filled with templates. This could hinder the ability to distinguish between different elements that make up a full page on the screen. Template functions can help eliminate these issues.

Data template functions can be enhanced even further by implementing the concepts of metaprogramming. In a co-authored book, “Metaprogramming in .NET,” Jason Bock (Microsoft MVP, Magenic Principal Consultant) and Kevin Hazzard (Microsoft MVP, Consultant) do an excellent job at explaining the fundamental concepts of metaprogramming. By using these concepts, template functions can be created in a way that allows maximum flexibility, maintainability, and scalability as more scenarios are needed.

In the following sections, each topic will be described along with code snippets and screen captures of the resultant display. For a more interactive experience, please download the usable solution available at the end of this article. This solution contains a functional version of the code snippets provided. Please note, the solution is meant to be run in a Windows 8 development environment.

Data Template Functions

Up to this point, the data templates described have been static. Nested templates added some flexibility but even then, the templates were static. Template functions offer numerous advantages over the standard static data templates. One particular advantage is that it allows the template to be dynamically created based on some criteria. For example, using static templates for handling different scenarios, multiple templates are required in markup to handle each scenario. This can introduce cluttered markup files. In addition, this can hinder the ability and elegance of scaling as more scenarios need to be handled. Using template functions, the adequate template can be created based on the current scenario saving the need for predefined template markup. Template functions allow this logic to be developed in a clean, straightforward manner completely driven by JavaScript code. The implementation of the template function can be integrated into the design and architecture of the app, greatly improving scalability while offering a potential reduction in maintenance efforts.

Embracing the power of JavaScript, template functions can be used to present data in a way that will not clutter an html file. In the sections below, JavaScript will be used to create data template functions that will be used for displaying a collection of data objects in a ListView.

The Markup

The standard ListView control will be used to present the data. Below is the markup declaring this:

<div id="listView" data-win-control="WinJS.UI.ListView"></div>

All that is needed is the above markup for this scenario. Notice the template is excluded from the markup. This is because the template will be created using JavaScript. Before the template is explained, the data source will be defined.

The Data Source

This scenario needs multiple data objects. The snippet below uses a new WinJS.Binding.List to represent a few of Magenic’s “Portfolio” items as they were at the time of this writing.

var portfolio = new WinJS.Binding.List( [

  {type: "Case Studies", details: "Payment Solution Organization"},

  {type: "White Papers", details: "SQL 2012: Bringing \"Big Data\" to the Desktop"},

  {type: "Videos", details: "Choosing a Development Partner"}

] );

WinJS.Namespace.define( "Data", {

  Portfolio: portfolio,

} );

A bindable portfolio object is created with the given collection of data objects as its items. A namespace is then created to provide access to the object in the form of Data.Portfolio.

The markup and data source have been created. The last component is the binding mechanism. The next section will explore in detail the necessary code needed to complete this data-binding scenario.

The Binding

With nested templates, the data-binding scenario includes gathering the markup elements, calling the WinJS.UI.process method, handling the Promise objects, and manually iterating over the data collections. Using a data template function, the work relies on the render target (in this case the ListView) and data (defined above). The below code snippet shows the implementation of a simple template function:

var listView = element.querySelector( ".win-listview" ).winControl;

listView.itemDataSource = Data.Portfolio.dataSource;

listView.itemTemplate = function ( itemPromise ) {

    //<div class="box list-item">

    // <h3 class="column-one row-one" data-win-bind="innerText: type;"></h3>

    // <em class="column-one row-two space" data-win-bind="innerText: details;"></em>

    //</div>

    //wait for data, then create & fill template

    return itemPromise.then( function ( dataItem ) {

        //get item data

        var data = dataItem.data;

        //create elements

        var container = document.createElement( "div" );

        var heading = document.createElement( "h3" );

        var details = document.createElement( "div" );

        //add classes for styling

        WinJS.Utilities.addClass( container, "box list-item" );

        WinJS.Utilities.addClass( heading, "column-one row-one" );

        WinJS.Utilities.addClass( details, "column-one row-two space" );

        //fill template elements with data values

        heading.innerText = data.type;

        details.innerText = data.details;

        //add template elements to parent element

        container.appendChild( heading );

        container.appendChild( details );

        //return parent element

        return container;

    } );

};

There is a lot going on here so I will break it down. First, a local reference to the ListView is created.

var listView = element.querySelector( ".win-listview" ).winControl;

There are a few important things to note. One, querySelector is called from the page element instead of the document. This way there is a reduction in the number of elements to iterate to find the list view. The next thing to note is that the “winControl” property is used off from the result of the call to querySelector. This contains the actual WinJS.UI.ListView control which is not defined until the page is processed.

The ListView reference contains two properties (itemDataSource and itemTemplate) that are essential to working with data template functions. The itemDataSource is simply assigned the dataSource property of the bindable portfolio object as shown below.

listView.itemDataSource = Data.Portfolio.dataSource;

Next, the itemTemplate property is assigned an anonymous function.

listView.itemTemplate = function ( itemPromise ) { ...

The function takes a Promise object. This object will be used to easily retrieve a reference to the current data item and mark the time to create the template. Note, a data template function can also be declared in a different part of the code using a named function. The name of the function would then be assigned to the itemTemplate member of the ListView control.

A local ListView reference has been created and assigned a data source and template function. Now it’s time to examine the meat of this scenario, the insides of the data template function. The next steps require an understanding of what is expected of template functions. Template functions must return either a root DOM element, or an object with predefined properties. In this scenario a DOM element will be returned. Using the itemPromise’s ‘then’ method, the function will be called that constructs the DOM element, fills it with data, and returns it.

return itemPromise.then( function ( dataItem ) { ...

Using the itemPromise in this way will allow the template function to wait for the data before creating and populating the data template.

When the data has been retrieved, the itemPromise then calls the given function passing it the current data item. This data item will be used to populate the data template.

var data = dataItem.data;

The dataItem parameter contains a member called ‘data’. This member contains the actual data model. Here, a local instance of the data model is stored for later use.

The data has been retrieved and locally stored. Now, the template will be created. Using the common document.createElement method, two div elements and a heading element are created.

var container = document.createElement( "div" );

var heading = document.createElement( "h3" );

var details = document.createElement( "div" );

These elements will make up the data template. The container is the root DOM element that will be returned. This container will contain the heading element as well as the details element.

WinJS comes with a lot of helpful methods accessible in the WinJS.Utilities namespace. These methods provide capability to add and remove DOM elements, start and stop logging, and add and remove classes. In this scenario, the WinJS.Utilities.addClass method will be used.

WinJS.Utilities.addClass( container, "box list-item" );

WinJS.Utilities.addClass( heading, "column-one row-one" );

WinJS.Utilities.addClass( details, "column-one row-two space" );

More information about the addClass method can be found on MSDN: “WinJS.Utilities.addClass function”. The use of this method is not needed for data template functions but it will help make the data templates fit within the look and feel of the app.

The next step is to fill the necessary DOM elements with the locally stored data model.

heading.innerText = data.type;

details.innerText = data.details;

Here, the heading’s text is set to the portfolio item’s type and the details’ text is set to the portfolio item’s details member.

Finally, the heading and details elements are appended to the container which is then returned.

container.appendChild( heading );

container.appendChild( details );

return container;

With the container created and filled with the populated template elements, the container is returned. This marks the end of the data template function for this scenario.

The Outcome

With all of the pieces in place, running the app would produce results similar to what is shown in Figure 1 depending on the chosen styling and ListView settings.

Styled outcome of using a data template function

Figure 1 – Styled outcome of using a data template function

As can be seen in Figure 1, the results do not appear to be anything different from normal usage of a ListView, but the template system used is far more powerful. More information can be found by reading the MSDN article, “How to create a templating function”. This article includes details about template functions and when one must be used.

Conclusion

The potential of data template functions easily stretch beyond added scalability. They give the developer complete control over the presentation of each data object. By taking template functions into account when designing Windows Store Apps, the app will have the potential of a very powerful data presentation layer.

Categories

© 2013 Magenic, All rights Reserved