Adding data to a project template

7 out of 15 rated this helpful - Rate this topic

In the Grid and Split templates, the code that obtains the data needed for the app is in the data.js file. This file represents a sample data source for the app. The data.js file includes static data that you typically need to replace with dynamic data. For example, if your app makes a single xhr request to obtain RSS data, you may want to include this code in data.js. Including the code there enables you to easily use your own data without changing the data model present in the templates.

You need to be aware of a few things when you add your own data to an app:

  • Groups and items are linked intrinsically. The app expects item data to be organized in groups. You can unlink the two in your own implementation, but you need to modify code to make that implementation work. This topic shows how groups are used in the template data model.
  • When you implement custom data for an app in data.js, you need to make sure that property names inherent to your custom data are mapped to the property names that are used by the template. You can change the names used by the template, but this requires more code revision. This topic shows a few examples of how to do that.

Items and groups

Template data is stored in a WinJS.Binding.List. This code shows the declaration of a list in the data.js file.


var list = new WinJS.Binding.List();

An array of item data (sampleItems in this example) is passed to the WinJS.Binding.List by the push function, as shown here:


generateSampleData.forEach(function (item) {
    list.push(item);
});

WinJS.Binding.List includes internal logic to handle grouping of data. The sampleItems array includes a group property that identifies the group that the item belongs to (in the sample data, groups are specified in the sampleGroups array). Here is the array of item data in the generateSampleData function:


function generateSampleData() {
    // . . .
    var sampleGroups = [
        { key: "group1", title: "Group Title: 1", // . . .
        // . . .
    ];

    var sampleItems = [
        { group: sampleGroups[0], title: "Item Title: 1", // . . .
        // . . .
    ];

    return sampleItems;
}

When you modify the app for your custom data, you might want to follow the same pattern for grouping your data. For smaller data sets, we recommend that you use WinJS.Binding.List for ListView. If you don't group your items, you can still use a WinJS.Binding.List, but you need to modify template code wherever the template expects to find group-based data.

Tip  WinJS.Binding.List is a synchronous data source that uses a JavaScript array. For very large data sets, which could be a few thousand items, you may need to use an asynchronous data source. For more info, see Using ListView.

The createGrouped function of the WinJS.Binding.List specifies how to group the items by using a group key and an item group value. This function is called in data.js. key and group are both property names that are specified in the sample data arrays.


var groupedItems = list.createGrouped(
    function groupKeySelector(item) { return item.group.key; },
    function groupDataSelector(item) { return item.group; }
);

When the template app needs a list of items, it calls getItemsFromGroup, which returns a WinJS.Binding.List that contains only the items that belong to the specified group.


function getItemsFromGroup(group) {
    return list.createFiltered(function (item) {
        return item.group.key === group.key;
    });
}


The Windows Library for JavaScriptdefine function exposes the data for use in the app by specifying a namespace named Data along with a set of public member functions.


WinJS.Namespace.define("Data", {
    items: groupedItems,
    groups: groupedItems.groups,
    getItemReference: getItemReference,
    getItemsFromGroup: getItemsFromGroup,
    resolveGroupReference: resolveGroupReference,
    resolveItemReference: resolveItemReference
});

If you want to a different data source for each page in your app, or a different data model, you will need to replace all calls to these members in the JavaScript code.

Binding group and item data to the UI

In the templates, data is typically bound to the UI in the ready function that's defined in the .js file associated with each HTML page. The following code is contained in the ready function for split.js (the Split template). In this code, we obtain the ListView that's declared in split.html (a DIV element with a class name of itemlist).


var listView = element.querySelector(".itemlist").winControl;

The app obtains a group reference, and then calls getItemsFromGroup, which is implemented in data.js. As mentioned earlier, getItemsFromGroup returns a WinJS.Binding.List that contains only the items in the specified group.


this._group = (options && options.groupKey) ? Data.resolveGroupReference(options.groupKey) : 
    Data.groups.getAt(0);
this._items = Data.getItemsFromGroup(this._group);

In split.js, the group.title property is then used to specify the title of the page:


element.querySelector("header[role=banner] .pagetitle")
    .textContent = this.group.title;

We then bind the list returned from getItemsFromGroup to the ListView control, set the template, specify the handler for item selection (_selectionChanged), and use ListLayout to arrange the items in a vertical list.



listView.itemDataSource = this._items.dataSource;
listView.itemTemplate = element.querySelector(".itemtemplate");
listView.onselectionchanged = this._selectionChanged.bind(this);
listView.layout = new ui.ListLayout();


In the preceding code, the app associates a template with each item in the ListView, as illustrated here.


listView.itemTemplate = element.querySelector(".itemtemplate");

Windows Library for JavaScript templates, which are based on WinJS.Binding.Template, are used to format and display multiple instances of data. The most common template used in the Grid App and Split App is the item template that's used to display items in a ListView. Like every Windows Library for JavaScript template object, you declare it by adding a data-win-control attribute and setting the attribute to WinJS.Binding.Template. Here's the HTML code for the itemtemplate in split.html:


<div class="itemtemplate" data-win-control="WinJS.Binding.Template">
    <img class="item-image" src="#" data-win-bind="src: backgroundImage; 
        alt: title" />
    <div class="item-info">
        <h3 class="item-title win-type-ellipsis" 
            data-win-bind="textContent: title"></h3>
        <h6 class="item-subtitle win-type-ellipsis" 
            data-win-bind="textContent: subtitle"></h6>
        <h4 class="item-description" 
            data-win-bind="textContent: description"></h4>
    </div>
</div>


The itemtemplate is used for arbitrary ListView items. The ListView items might be groups or individual data items, depending on context. In items.html, for example, the ListView items are groups.

Important  Templates that you create by using WinJS.Binding.Template are not related to Visual Studio project and item templates, such as Grid and Split.

It is important to understand that the project templates expect certain properties to be present in the data, and these properties are explicitly named in the HTML. In the preceding HTML code for itemtemplate, you can find properties such as title, subtitle, and description. If your custom app data doesn't use these specific property names, you need to do one of the following:

  • Map your data to these property names (typically in data.js), or
  • Fix all the HTML and .js code references to these properties in the template code, so that they match the property names used in your data. The properties used in the templates include:
    • title, subtitle, description, and backgroundImage (group and item properties)
    • group and content (item properties)
    • key (group property)

Following the same Windows Library for JavaScript template pattern, the Grid App template also uses a headerTemplate in some of its HTML pages.

Example of binding data to the UI

This section shows how to implement your own data source in a project template. The sample code here uses an xhr request to generate RSS data.

Updating data.js

  1. Create a new project in Visual Studio. Use either the Split App or Grid App project template.
  2. In data.js, add the following variables near the beginning of the file, after the "use strict" statement:
    
    var lightGray = "data:image/png;base64,
        iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsQAAA7EAZUrDhsAAAANSURBVBhXY7h4+cp/AAhpA3h+ANDKAAAAAElFTkSuQmCC";
    var mediumGray = "data:image/png;base64,
        iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsQAAA7EAZUrDhsAAAANSURBVBhXY5g8dcZ/AAY/AsAlWFQ+AAAAAElFTkSuQmCC";
    
    
  3. In data.js, remove the generateSampleData function that contains these arrays: sampleGroups and sampleItems.

    We'll replace this data with RSS data. We won't be using most of the placeholder variables like groupDescription, but we re-use placeholder images, lightGray and mediumGray, for the new code to work.

  4. In the same location where you removed generateSampleData, add the following code to data.js:
    
    
    function getFeeds() {
        // Create an object for each feed.
        blogs = [
            {
                key: "blog1", url: 
           'http://windowsteamblog.com/windows/b/developers/atom.aspx',
                title: 'tbd', subtitle: 'subtitle', updated: 'tbd',
                backgroundImage: lightGray,
                acquireSyndication: acquireSyndication, dataPromise: null
            },
            {
                key: "blog2", url: 
           'http://windowsteamblog.com/windows/b/windowsexperience/atom.aspx',
                title: 'tbd', subtitle: 'subtitle', updated: 'tbd',
                backgroundImage: lightGray,
                acquireSyndication: acquireSyndication, dataPromise: null
            }]
        // Get the content for each feed in the blog's array.
        blogs.forEach(function (feed) {
            feed.dataPromise = feed.acquireSyndication(feed.url);
            dataPromises.push(feed.dataPromise);
        });
    
        // Return when all asynchronous operations are complete
        return WinJS.Promise.join(dataPromises).then(function () {
            return blogs;
        });
    
    };
    
    function acquireSyndication(url) {
        return WinJS.xhr({ url: url });
    }
    
    function getBlogPosts() {
        getFeeds().then(function () {
            // Process each blog.
            blogs.forEach(function (feed) {
                feed.dataPromise.then(function (articlesResponse) {
                    var articleSyndication = articlesResponse.responseXML;
                    // Get the blog title and last updated date.
                    feed.title = articleSyndication.querySelector(
                        "feed > title").textContent;
                    var ds = articleSyndication.querySelector(
                        "feed > updated").textContent;
                    var date = ds.substring(5, 7) + "-" +
                        ds.substring(8, 10) + "-" + ds.substring(0, 4);
                    feed.updated = "Last updated " + date;
                    // Process the blog posts.
                    getItemsFromXml(articleSyndication, blogPosts, feed);
                });
            });
        });
    
        return blogPosts;
    }
    
    function getItemsFromXml(articleSyndication, blogPosts, feed) {
        var posts = articleSyndication.querySelectorAll("entry");
        // Process each blog post.
        for (var postIndex = 0; postIndex < posts.length; postIndex++) {
            var post = posts[postIndex];
            // Get the title, author, and date published.
            var postTitle = post.querySelector("title").textContent;
            var postAuthor = post.querySelector("author > name").textContent;
            var pds = post.querySelector("published").textContent;
            var postDate = pds.substring(5, 7) + "-" + pds.substring(8, 10)
                + "-" + pds.substring(0, 4);
            // Process the content so that it displays nicely.
            var staticContent = toStaticHTML(post.querySelector(
                "content").textContent);
            // Store the post info we care about in the array.
            blogPosts.push({
                group: feed, key: feed.title, title: postTitle, 
                author: postAuthor, pubDate: postDate, 
                backgroundImage: mediumGray, content: staticContent
            });
        }
    }
    
    

    If you want more info on the xhr code used to generate the data, see the following tutorial, Part 3: Create a Blog Reader.

  5. In data.js, replace this code:
    
    var list = new WinJS.Binding.List();
    var groupedItems = list.createGrouped(
        function groupKeySelector(item) { return item.group.key; },
        function groupDataSelector(item) { return item.group; }
    );
    
    // TODO: Replace the data with your real data.
    // You can add data from asynchronous sources whenever it becomes available.
    generateSampleData.forEach(function (item) {
        list.push(item);
    });
    
    

    with this:

    
    var dataPromises = [];
    var blogs;
    
    var blogPosts = new WinJS.Binding.List();
    
    var list = getBlogPosts();
    var groupedItems = list.createGrouped(
        function groupKeySelector(item) { return item.group.key; },
        function groupDataSelector(item) { return item.group; }
    );
    
    

    We'll re-use the code in createGrouped that specifies the grouping—the groupKeySelector and groupDataSelector functions.

    Because we've changed some of the property names used in the templates, we'll need to make a few updates to the HTML pages. Specifically, for any subtitle properties that reference an item (not a group) we need to change subtitle to author. For any description properties that reference an item, we need to change description to pubDate.

    To implement these changes in the UI, see one of the following sections:

Binding example data to the UI in the Split template

  1. To use the example code in the Split template, open split.html.
  2. In split.html, we need to changes some lines in the DIV element that has a class name of itemtemplate. Change these lines:
    
    
    <h6 class="item-subtitle win-type-ellipsis" 
        data-win-bind="textContent: subtitle"></h6>
    <h4 class="item-description" data-win-bind="textContent: description"></h4>
    
    
    

    to this:

    
    <h6 class="item-subtitle win-type-ellipsis" 
        data-win-bind="textContent: author"></h6>
    <h4 class="item-description" data-win-bind="textContent: pubDate"></h4>
    
    
  3. Also in split.html, the article section (articleSection) has header information that we need to update. Change this line:
    
    <h4 class="article-subtitle" data-win-bind="textContent: subtitle"></h4>
    
    

    to this:

    
    <h4 class="article-subtitle" data-win-bind="textContent: author"></h4>
    
    
  4. Open items.html.

    The Windows Library for JavaScript item template defined in the HTML code contains arbitrary ListView items. In items.html, the template is used to show groups (blogs). The only group property we need to change here is subtitle.

    
    <h6 class="item-subtitle win-type-ellipsis" 
        data-win-bind="textContent: subtitle"></h6>
    
    
  5. Change the subtitle property to updated, as shown here:
    
    <h6 class="item-subtitle win-type-ellipsis"
        data-win-bind="textContent: updated"></h6>
    
    
  6. Save the project and press F5 to debug the app.

    You see the page title immediately, but there's a short delay while the feed data is retrieved. When all the promises are fulfilled, you see each blog in the home page. Click on one of the blogs to see the blog posts in master/detail view.

Binding example data to the UI in the Grid template

  1. To use the RSS example code in the Grid template, open groupDetail.html.

    This page displays a single group (a blog) and the individual items (blog posts) that are part of the group.

  2. In groupDetail.html, we need to change some lines in the DIV element that has a class name of item-info. Change these lines:
    
    
    <h6 class="item-subtitle win-type-ellipsis"
        data-win-bind="textContent: subtitle"></h6>
    <h4 class="item-description" 
        data-win-bind="textContent: description"></h4>
    
    
    

    to this:

    
    <h6 class="item-subtitle win-type-ellipsis" 
        data-win-bind="textContent: author"></h6>
    <h4 class="item-description" 
        data-win-bind="textContent: pubDate"></h4>
    
    

    In groupDetail.html, the header template describes group information, not individual items. So we don't need to change the subtitle property. Here's the header template:

    
    
    <div class="headerTemplate" data-win-control="WinJS.Binding.Template">
        <h2 class="group-subtitle" data-win-bind="textContent: subtitle"></h2>
        <img class="group-image" src="#" 
            data-win-bind="src: backgroundImage; alt: title" />
        <h4 class="group-description" data-win-bind="innerHTML: description"></h4>
    </div>
    
    
  3. However, we don't have a description property for each group (we only have this property for items), so we need to change this property to updated in the preceding code, as shown here:
    
    <h4 class="group-description" data-win-bind="innerHTML: updated"></h4>
    
    
  4. Open groupedItems.html, which displays all the groups and their individual blog posts.
  5. In this page, the generic Windows Library for JavaScript item template displays individual items (blog posts), so we need to update the subtitle property. Change this:
    
    <h6 class="item-subtitle win-type-ellipsis" 
        data-win-bind="textContent: subtitle"></h6>
    
    

    to this:

    
    <h6 class="item-subtitle win-type-ellipsis" 
        data-win-bind="textContent: author"></h6>
    
    
  6. We also need to make one change in itemDetail.js. The ready function references the subtitle property. In this case, the subtitle refers to an item, not a group, so we need to change it to pubDate. Change this:
    
    element.querySelector("article .item-subtitle").textContent = item.subtitle;
    element.querySelector("article .item-image").src = item.backgroundImage;
    element.querySelector("article .item-image").alt = item.subtitle;
    
    

    to this:

    
    element.querySelector("article .item-subtitle").textContent = item.pubDate;
    element.querySelector("article .item-image").src = item.backgroundImage;
    element.querySelector("article .item-image").alt = item.pubDate;
    
    
  7. Save the project and press F5 to debug the app.

    You see the page title immediately, but there's a short delay while the feed data is retrieved. When all the promises are fulfilled, you see the items in each blog in the home page. Click a group heading to see the group page, or click on an item to see an individual blog post.

Related topics

JavaScript project templates for Windows Store apps
JavaScript item templates for Windows Store apps
Adding data to a project template (C#, VB, and C++)

 

 

Build date: 3/11/2013

Did you find this helpful?
(1500 characters remaining)
© 2013 Microsoft. All rights reserved.