April 28, 2015 // By Caleb McElrath
Windows Library for JavaScript (WinJS) provides convenient ways to format and display data. Here we focus on templates and a few ways of implementing them in a Metro app. Templates provide a way to present multiple instances of object data to the user with a high degree of control while maintaining ease of implementation. Templates can be used with ListViews, other controls provided for Metro app development, and can even be used without a predefined view.
In this article, three ways of using templates will be described, beginning with template basics. This section will provide an overview of templates and what roles HTML and WinJS play. The second section will describe item templates and how they can be used to display a list of items. Then, building off the second section, the third section will employ item templates with the ListView control. Finally, more advanced topics will be discussed, including nested templates and object-oriented CSS. A usable solution that contains a functional version of the code snippets provided is available at the end of the post. General data-binding experience is assumed.
Template Basics
There are four necessary components when working with templates: Template, Data Source, Binding, and Render Target. Templates play a major role in data-binding. They provide the presentation-level structure of the data. The data is what will be structured by the template. WinJS is used to bind the template and data so when the page is processed, the data is displayed in the structure enforced by the template. During page processing, the resultant HTML is placed in the render target. The following sample will demonstrate the use of a basic template.
Sample 1: Single Data Object Template
This sample will use a template for a single data object. Each of the four necessary components will be described starting with the markup.
The Markup
Below is the essential markup for using templates:
<div id="service-template" data-win-control="WinJS.Binding.Template"> <a class="service-link" data-win-bind="innerText: type; href: url; style.color: color"></a> </div> <div class="scenario fragment"> <section aria-label="Main content" role="main"> <div id="service-target"></div> </section> </div>
Let’s examine this in more detail. There’s more going on than what is initially perceived. Below is the template used to display a link using the properties of the data source. The notable points are the data-win-control and data-win-bind attributes. The data-win-control attribute specifies the WinJS control to use. A div element must be used when specifying a control. The control here is WinJS.UI.Binding.Template which is used to specify a template. Custom controls can be created and used in a similar manner. More information on WinJS controls can be found in the MSDN documentation: “Quickstart: adding WinJS controls and styles”. The content within the WinJS.Binding.Template is what will actually be used in data-binding. The data-win-bind attribute of the <a> tag specifies the <element property> to <object property> binding. For example, the ‘innerText’ property of the <a> element is bound to the ‘type’ property of the data source. This way, when the page is processed, the text of the <a> element will contain the value of the data source’s ‘type’ property. More information will be given below regarding the details of binding. First, let’s take a look at the render target markup:
<div id="service-target"></div>
The render target is actually quite straightforward; it is where our template data will be displayed. It is a simple <div> element with an id for easy access from JavaScript. The render target doesn’t necessarily have to be a <div> element.
Now that the markup is in place, we need data to display! The next section will describe the data object used as the data source in this data-binding scenario.
The Data Source
One of the simplest ways to create a data source is to define a bindable object by using the WinJS.Binding.define function. This function takes an object representing the pattern for defining the properties of the bindable object (as defined on MSDN: “WinJS.Binding.define function”). Below is an example of using WinJS.Binding.define to define a bindable object called ‘service.’ The data source contains type, url, and color properties all initialized to empty strings. This object will be used to represent one of the numerous services that Magenic provides. Next, a namespace is created to access the data source in the form of Data.Service.
var service = new WinJS.Binding.define({ type: "", url: "", color: "" }); WinJS.Namespace.define("Data", { Service: new service({ type: "Custom Software Development", url: "http://magenic.com/Services/CustomSoftwareDevelopment.aspx", color: "#7AB800" }) });
As you can see, the service object is created using the WinJS.Binding.define function. Then the namespace is created to access the service object in the form of Data.Service. Note the creation of a new service object. This is necessary because the WinJS.Binding.define function actually returns a constructor. This constructor takes an object matching the property pattern given upon definition but with its properties populated with values. The object referred to by Data.Service now contains a new service object with the given property values.
We have the template, render target, and data but, we still need to bind the template and data. This is done during page processing and is executed in WinJS.
The Binding
There are many ways to set up data-binding. This particular case uses the WinJS.UI.process method. This method applies control binding to the specified element. The specified element is the template previously created in the section “The Markup”. Since the WinJS.UI.process method returns a Promise, the ‘then’ method can be used to render the data. More information about a Promise and its ‘then’ method can be found on MSDN: WinJS.Promise object. Here, the ‘then’ method takes a function whose parameter is the template control. When render is called on the template, the relevant fields of the data source are bound to the template. Below is the JavaScript that does this:
ready:function(element, options) { var template = document.getElementById("service-template"); var renderTarget = document.getElementById("service-target"); WinJS.UI.process(template).then(function(templateControl) { templateControl.render(Data.Service, renderTarget); }); }
You may have noticed the processing is done within a function called “ready”. I won’t be getting into the details of the various aspects of the app’s lifecycle, but this particular function is important. This function is called during an initial activation and after the app’s splash screen is torn down (as described on MSDN: “How to handle app activation”). During this event, the UI is set up and available, or “ready” to be used. We begin with retrieving the template element. Since an id was specified, document.getElementById is used. Next the render target is retrieved using the same method. Please note that there are alternate ways of accessing elements by using the querySelector and querySelectorAll methods. I won’t be using these methods in this article, in hopes to exemplify the ability to apply web development skills you may already have towards developing Windows 8 Metro apps using HTML and JavaScript.
With the template and the render target elements stored, the processing can begin! As described before, the WinJS.UI.process method is used. The ‘then’ method takes a function containing the templateControl which is used to render the data object to the given render target.
The Outcome
With the four components in place, the data-binding implementation is complete. Below is a small screen capture of what the processed template will look like:
Depending on your choice of CSS, this may be a little different than what you will see. Though, based on the given four components, the page contains an <a> tag with the text of “Custom Software Development” displayed in green and links to http://magenic.com/Services/CustomSoftwareDevelopment.aspx which, at the time of this writing, describes Magenic’s Custom Software Development service offering. The WinJS.UI.process method is used to bind the template control with the given data and render it on the page. Using a template in this way makes it easy to update the content in the UI merely by changing the underlying data. This prevents the need to recompile and redeploy the app whenever the content changes (which developers and users alike can appreciate).
Now that the essentials of Metro app templates have been described, it is time to take a look at item templates.
Item Templates
Item templates are useful for displaying multiple instances of an object. Basically, a data source can be a collection of objects. An item template can be used to display each of the objects in the collection without having to manually repeat the presentation of this data. This reduces development time while maintaining the ability to update the UI content without the need to redeploy the app. I’ll highlight the necessary changes to the components developed in Sample 1 to achieve this.
Sample 2: Item Templates for Data Object Collections
This sample will use an item template for a collection of data objects. This will be very similar to Sample 1 but with some important differences beginning with the markup.
The Markup
To keep the samples simple, the template will be changed to a <div> element with corresponding changes to the data-win-bind values. Nothing will be done with the service-target <div> element. Below is the updated markup:
<div id="service-template" data-win-control="WinJS.Binding.Template"> <div class="service-type" data-win-bind="innerText: type"></div> </div> <div class="scenario fragment"> <section aria-label="Main content" role="main"> <div id="service-target"></div> </section> </div>
The Data Source
The first change is with the data. We need multiple instances of a data object. This can be accomplished by creating a new WinJS.Binding.List. More information can be found on MSDN: “WinJS.Binding.List object”. Basically, this method creates a bindable list of the given objects. Here we have seven items, each representing a different service offering of Magenic:
var services = new WinJS.Binding.List([ { id: "1", type: "Custom Software Development" }, { id: "2", type: "QA & Testing" }, { id: "3", type: "Support & Maintenance" }, { id: "4", type: "User Experience Design" }, { id: "5", type: "Mobile Application Development" }, { id: "6", type: "Cloud Computing Development" }, { id: "7", type: "Application Integration Services" } ]); WinJS.Namespace.define("Data", { Services: services });
A bindable services object is created with the given collection of data objects as its items. A namespace is then created as in Sample 1, but with two major differences: 1 – the bindable services object is used for assignment directly instead of calling a constructor, and 2 – the object is accessed through Data.Services.
The markup is the same as Sample 1. All that’s left is binding the template and data.
The Binding
As in The Binding in Sample 1, the template and render target elements are retrieved. Then the WinJS.UI.process method is called passing in the template element. The difference when working with a bindable list is in the ‘then’ function where an iteration of the list occurs. With each item in Data.Services, the render method of the template control is called, passing in the current list item’s data and the render target.
WinJS.UI.process(template).then(function(templateControl) { for (var i = 0; i < Data.Services.length; i++){ templateControl.render(Data.Services.getItem(i).data, renderTarget); } });
Since the data source is a list, a for-loop is used to iterate over the items. The bindable list contains a method called getItem. This method returns an object containing the ‘data’ property. This property is where the actual data is stored for each list item. It is passed into the render method of the template control along with the render target element. This is how the template is used to render each item in the data source.
Outcome
The four components have now been created. Depending on your styling, running the app would produce results similar to the following:
A sample outcome of using an item template to display a collection of data is depicted in the small screen capture above. Using an item template in this way allows complete control of the presentation and behavior provided to the user.
Item templates can be used in predefined views as well. In the following section, item templates will be used to display the items in a ListView control.
ListView Item Templates
The ListView control is provided as a predefined view for presenting items in a list with some default functionality. MSDN contains helpful examples of using ListViews: “Quickstart: Adding a ListView”.
The item template was introduced in Sample 2 and will be applied here as well. Each item in the data source will be displayed based on the given item template, though with a few differences.
Sample 3: Item Templates for ListViews
This sample will use an item template to display a collection of data objects in a ListView control. This will be very similar to Sample 2 but with some important differences beginning with the render target markup.
The Markup
The only difference in the markup compared to Sample 2 is the render target. In this case, the target is actually a predefined view called the ListView. As we saw in Sample 1, controls are defined using a <div> element containing a data-win-control attribute with the name of the control to use. This time we use the WinJS.UI.ListView control. Note that the data-win-bind attribute is not used in this markup. This is intentional because the markup is the render target, not a template. The markup below defines the ListView control:
<div id="service-listview" data-win-control="WinJS.UI.ListView"></div>
The ListView is created using the data-win-control attribute of a <div> element. This element is given an id of “service-listview” for easy access in JavaScript.
The next difference is in the binding. We’ll take a look at this in the following section.
The Binding
There are a few ways to set up binding for ListViews. We will use a more implicit approach and will not be calling WinJS.UI.process. Instead, the ListView control is obtaining and its itemTemplate and itemDataSource properties are set accordingly.
var template = document.getElementById("service-template"); var renderTarget = document.getElementById("service-listview").winControl; renderTarget.itemTemplate = template; renderTarget.itemDataSource = Data.Services.dataSource;
Above is an example of using the itemTemplate and itemDataSource property of the ListView control to set up data-binding. The ListView control was obtained through the winControl property of the DOM element containing the corresponding WinJS.UI.ListView data-win-control value. Then the item template is retrieved. This is assigned to the ListView’s itemTemplate property. Since WinJS.Binding.List was used to create the services object, the dataSource property is provided. This property is assigned to the itemDataSource of the ListView. The four components are now in place.
Outcome
The small screen capture below depicts the relevant section of the page after it has been processed. When the ListView control is rendered, the item template is used to present each item in the data source. Each item is presented in the order it was added in the data source. The ListView contains some basic functionality as well.
With adequate styling, the list can be rendered in an appealing manner along with the default functionality of a ListView. Using item templates for ListViews will greatly reduce development efforts while providing some basic, yet essential functionality.
Conclusion
The data binding abilities provided by WinJS allow data to be presented in a simple, reusable way. With adequate data binding, as the data changes, the user interface will update accordingly.
There are a few more advanced possibilities to explore in regards to templates and UI including nested templates, template functions, and Object-Oriented CSS. Nested templates provide further flexibility while item template functions give you the most control during data-binding. Styling your resultant HTML is easy enough, but maintenance can be a pain. The concept of Object-Oriented CSS comes to the rescue! Adequately implementing the principles of OO CSS will greatly reduce the overall maintenance of the app while enhancing the user experience by enforcing cohesiveness and fluidity.
With a combination of flexible binding methods, data structures, and templates, robust data-driven Metro apps can be created with ease. For more information about the development impacts of Windows 8, see Magenic’s white paper, “Assessing the Windows 8 Development Platform,” co-authored by six of Magenic’s principal consultants and Microsoft MVPs.