Metro: Grouping Items in a ListView Control

The purpose of this blog entry is to explain how you can group list items when displaying the items in a WinJS ListView control. In particular, you learn how to group a list of products by product category.

image

Displaying a grouped list of items in a ListView control requires completing the following steps:

  1. Create a Grouped data source from a List data source
  2. Create a Grouped Header Template
  3. Declare the ListView control so it groups the list items

Creating the Grouped Data Source

Normally, you bind a ListView control to a WinJS.Binding.List object. If you want to render list items in groups, then you need to bind the ListView to a grouped data source instead.

The following code – contained in a file named products.js — illustrates how you can create a standard WinJS.Binding.List object from a JavaScript array and then return a grouped data source from the WinJS.Binding.List object by calling its createGrouped() method:

(function () {
    "use strict";

    // Create List data source
    var products = new WinJS.Binding.List([
            { name: "Milk", price: 2.44, category: "Beverages" },
            { name: "Oranges", price: 1.99, category: "Fruit" },
            { name: "Wine", price: 8.55, category: "Beverages" },
            { name: "Apples", price: 2.44, category: "Fruit" },
            { name: "Steak", price: 1.99, category: "Other" },
            { name: "Eggs", price: 2.44, category: "Other" },
            { name: "Mushrooms", price: 1.99, category: "Other" },
            { name: "Yogurt", price: 2.44, category: "Other" },
            { name: "Soup", price: 1.99, category: "Other" },
            { name: "Cereal", price: 2.44, category: "Other" },
            { name: "Pepsi", price: 1.99, category: "Beverages" }
        ]);

    // Create grouped data source
    var groupedProducts = products.createGrouped(
        function (dataItem) {
            return dataItem.category;
        },
        function (dataItem) {
            return { title: dataItem.category };
        },
        function (group1, group2) {
            return group1.charCodeAt(0) - group2.charCodeAt(0);
        }
    );

    // Expose the grouped data source
    WinJS.Namespace.define("ListViewDemos", {
        products: groupedProducts
    });
})();

Notice that the createGrouped() method requires three functions as arguments:

  1. groupKey – This function associates each list item with a group. The function accepts a data item and returns a key which represents a group. In the code above, we return the value of the category property for each product.
  2. groupData – This function returns the data item displayed by the group header template. For example, in the code above, the function returns a title for the group which is displayed in the group header template.
  3. groupSorter – This function determines the order in which the groups are displayed. The code above displays the groups in alphabetical order: Beverages, Fruit, Other.

Creating the Group Header Template

Whenever you create a ListView control, you need to create an item template which you use to control how each list item is rendered. When grouping items in a ListView control, you also need to create a group header template. The group header template is used to render the header for each group of list items.

Here’s the markup for both the item template and the group header template:

<div id="productTemplate" data-win-control="WinJS.Binding.Template">
    <div class="product">
        <span data-win-bind="innerText:name"></span>
        <span data-win-bind="innerText:price"></span>
    </div>
</div>

<div id="productGroupHeaderTemplate" data-win-control="WinJS.Binding.Template">
    <div class="productGroupHeader">
        <h1 data-win-bind="innerText: title"></h1>
    </div>
</div>

You should declare the two templates in the same file as you declare the ListView control – for example, the default.html file.

Declaring the ListView Control

The final step is to declare the ListView control. Here’s the required markup:

<div data-win-control="WinJS.UI.ListView"
    data-win-options="{
        itemDataSource:ListViewDemos.products.dataSource,
        itemTemplate:select('#productTemplate'),
        groupDataSource:ListViewDemos.products.groups.dataSource,
        groupHeaderTemplate:select('#productGroupHeaderTemplate'),
        layout: {type: WinJS.UI.GridLayout}
    }">
</div>

In the markup above, six properties of the ListView control are set when the control is declared. First the itemDataSource and itemTemplate are specified. Nothing new here.

Next, the group data source and group header template are specified. Notice that the group data source is represented by the ListViewDemos.products.groups.dataSource property of the grouped data source.

Finally, notice that the layout of the ListView is changed to Grid Layout. You are required to use Grid Layout (instead of the default List Layout) when displaying grouped items in a ListView.

Here’s the entire contents of the default.html page:

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>ListViewDemos</title>

    <!-- WinJS references -->
    <link href="//Microsoft.WinJS.0.6/css/ui-dark.css" rel="stylesheet">
    <script src="//Microsoft.WinJS.0.6/js/base.js"></script>
    <script src="//Microsoft.WinJS.0.6/js/ui.js"></script>

    <!-- ListViewDemos references -->
    <link href="http://3a0ojxkezud2pogen3j4z0w18nq.wpengine.netdna-cdn.com/css/default.css" rel="stylesheet">
    <script src="http://3a0ojxkezud2pogen3j4z0w18nq.wpengine.netdna-cdn.com/js/default.js"></script>
    <script src="http://3a0ojxkezud2pogen3j4z0w18nq.wpengine.netdna-cdn.com/js/products.js" type="text/javascript"></script>

    <style type="text/css">

        .product {
            width:  200px;
            height:  100px;
            border:  white solid 1px;
            font-size:  x-large;
        }
    </style>
</head>
<body>
    <div id="productTemplate" data-win-control="WinJS.Binding.Template">
        <div class="product">
            <span data-win-bind="innerText:name"></span>
            <span data-win-bind="innerText:price"></span>
        </div>
    </div>

    <div id="productGroupHeaderTemplate" data-win-control="WinJS.Binding.Template">
        <div class="productGroupHeader">
            <h1 data-win-bind="innerText: title"></h1>
        </div>
    </div>
    <div data-win-control="WinJS.UI.ListView"
        data-win-options="{
            itemDataSource:ListViewDemos.products.dataSource,
            itemTemplate:select('#productTemplate'),
            groupDataSource:ListViewDemos.products.groups.dataSource,
            groupHeaderTemplate:select('#productGroupHeaderTemplate'),
            layout: {type: WinJS.UI.GridLayout}
        }">
    </div>
</body>
</html>

Notice that the default.html page includes a reference to the products.js file:

<script src=”/js/products.js” type=”text/javascript”></script>

The default.html page also contains the declarations of the item template, group header template, and ListView control.

Summary

The goal of this blog entry was to explain how you can group items in a ListView control. You learned how to create a grouped data source, a group header template, and declare a ListView so that it groups its list items.

    Discussion

    Comments are closed.