JavaScript

使用 JavaScript 在 Windows 应用商店应用程序中进行数据绑定

Chris Sells
Brandon Satrom

下载代码示例

在本文中我们将探讨使用 JavaScript 生成 Windows 存储应用程序中的数据绑定。 这是其他类型的应用程序生成为 Windows 8 的混合体。 就像一个桌面应用程序,它安装在您的计算机,与不同的网站上。 另一方面,它是像一个 Web 站点,您可以生成它使用 HTML5、 JavaScript 和 CSS3。 然而,而不是生成的服务器端上的用户界面,用于生成 Windows 存储应用程序和基础 Windows 运行时的 JavaScript 框架允许您构建与客户端状态、 脱机存储、 控件、 模板和绑定的应用程序 — — 以及其他服务的整个主机。

数据绑定是能够采取的值列表中的数据如一组的 RSS 源,并使用它来填充控件,如列表视图。 我们可以定制我们提取使用模板的数据。 我们将介绍如何绑定是更具弹性,允许更新 UI 的作为基础的数据更改,以及排序、 筛选和分组。

当我们使用数据绑定时,往往是在控件的上下文中。 我们要去看一看列表视图和其支持的数据绑定和模板。

数据绑定 101

使用绑定允许您设置在两个对象,最常作为关联对象与 HTML 文档对象模型 (DOM) 元素的属性的属性的两个属性之间的关联, 图 1 显示。

Binding Between an Attribute on a Destination Element and a Property from a Source Object
图 1 的目标元素中的属性和属性之间的绑定源对象从

建立具有约束力的目的是要涉及的两个对象之间复制数据:从数据的来源和目标位置的数据是源。 你可能会认为您可以完成这一目标使用简单的赋值语句:

myDestElem.value = mySourceObj.
name;

从根本上说,分配是什么绑定不会。 然而,绑定不是只是说明哪些数据复制和到哪里去,而且还当。 具有约束力的"时"通常是下列之一:

  • 单向绑定:对象更改时,将数据复制到的 DOM 元素。 这是默认的 Windows 库中的 JavaScript (WinJS) 中。
  • 一次性绑定:首先建立绑定,请将数据复制到 DOM 元素中。 这相当于工作分配。
  • 双向绑定:当对象发生更改时,将数据复制到的 DOM 元素和 DOM 元素更改时,将数据复制到该对象。 这并不支持 WinJS。

默认情况下,WinJS 绑定是单向的绑定,虽然也支持一次性绑定。 虽然 WinJS 不支持双向绑定,那里是钩在您最喜爱的双向绑定引擎,如在 jQuery,一个好地方如您所见。

将对象绑定

若要开始,让我们说我们要在我们的生活,人民建立一个小小的浏览器中所示图 2

将对象绑定到一组 HTML 元素的图 2

想法是我们可以有很多人通过其中我们可以导航,每个都有一个名称、 年龄和最喜爱的颜色。 我们在使用上一页和下一页按钮,我们导航到其他人的列表中,如果我们按中间的按钮 — — 时钟 — — 我们通过增加当前显示人的年龄来庆祝生日。 以下表示人,我们会在我们的示例浏览一的组:

var people = [
  { name: "John", age: 18, favoriteColor: "red" },
  { name: "Tom", age: 16, favoriteColor: "green" },
  { name: "Chris", age: 42, favoriteColor: "blue" },
];

从 Visual Studio 2012 年的导航应用程序项目模板开始提供我们来填充,HTML,如中所示图 3

图 3 主窗体

<!DOCTYPE html>
<!-- homePage.html -->
<html>
<head>
  <meta charset="utf-8" />
  <title>homePage</title>
  <!-- WinJS references -->
  <link href="//Microsoft.WinJS.1.0/css/ui-light.css" rel="stylesheet" />
  <script src="//Microsoft.WinJS.1.0/js/base.js"></script>
  <script src="//Microsoft.WinJS.1.0/js/ui.js"></script>
  <link href="/css/default.css" rel="stylesheet" />
  <link href="/pages/home/home.css" rel="stylesheet" />
  <script src="/pages/home/home.js"></script>
</head>
<body>
  <!-- The content that will be loaded and displayed.
-->
  <div class="fragment homepage">
    <header aria-label="Header content" role="banner">
      <button class="win-backbutton" aria-label="Back"
        disabled type="button"></button>
      <h1 class="titlearea win-type-ellipsis">
        <span class="pagetitle">Welcome to PeopleBrowser!</span>
      </h1>
    </header>
    <section aria-label="Main content" role="main">
      <!-- display each person -->
      <div id="nameLabel">Name</div>
      <input id="name" readonly="true" type="text" 
        data-win-bind="value: name" />
      <div id="ageLabel">Age</div>
      <input id="age" readonly="true" type="text" 
        data-win-bind="value: age" />
      <div id="colorLabel">Favorite Color</div>
      <div id="color" data-win-bind="style.backgroundColor:
        favoriteColor"></div>
      <div id="buttons">
        <button id="previousButton"></button>
        <button id="birthdayButton"></button>
        <button id="nextButton"></button>
      </div>
    </section>
  </div>
</body>
</html>

此 HTML 有趣的部分是赢将数据绑定属性,使用下面的格式的使用:

<div data-win-bind="destProp1: sourceProp1; destProp2: sourceProp2;..."></div>

赢将数据绑定语法是以分号分隔的绑定表达式列表。 每个表达式是一个目的地 DOM 元素属性的组合和源对象属性。 我们使用上最喜爱的颜色 div 的样式设置背景色的虚线的语法 — — 即,源和目标,和它的属性 style.backgroundColor—works 钻到子对象,像预期的那样。

在绑定表达式中的每个目标属性被解决针对包含赢将数据绑定属性的 HTML 元素。 问题是:该对象从哪里来对属性名称解析的? 答案是:数据上下文。

设置数据上下文是绑定操作中,其中规定的 HTML 元素和作为数据上下文中,使用任何对象之间的关联,如中所示的一部分图 4

图 4 设置数据上下文

// homePage.js
(function () {
  "use strict";
  WinJS.UI.Pages.define("/pages/home/home.html", {
    ready: function (element, options) {
      var people = [
        { name: "John", age: 18, favoriteColor: "red" },
        { name: "Tom", age: 16, favoriteColor: "green" },
        { name: "Chris", age: 42, favoriteColor: "blue" },
      ];
      // Bind the current person to the HTML elements in the section
      var section = element.querySelector("section[role=main]");
      var current = 0;
      WinJS.Binding.processAll(section, people[current]);
    }
  });
})();

WinJS.Binding 命名空间中的 processAll 函数是为给定的 HTML 元素层次结构包含在我们的示例中的输入和 div 元素的节分析赢将数据绑定属性的功能。 对 processAll 的调用确立了 HTML 元素和每个赢将数据绑定属性中所述的数据上下文之间的绑定。 数据上下文是 processAll 的第二个参数,用于解决每个绑定表达式中找到的属性名称。 结果已经看上去像我们之后,如中所示图 2

默认情况下,如果我们做什么,我们已经建立的数据上下文对象和 DOM 元素之间的单向绑定连接。 这意味着作为数据源对象更改的属性,我们预期要更改以及,如中所示的输出图 5

图 5 作为属性的数据源更改,所以没有输出

function ready(element, options) {
  var people = [
    { name: "John", age: 18, favoriteColor: "red" },
    { name: "Tom", age: 16, favoriteColor: "green" },
    { name: "Chris", age: 42, favoriteColor: "blue" },
  ];
  var section = element.querySelector("section[role=main]");
  var current = 0;
  WinJS.Binding.processAll(section, people[current]);
  birthdayButton.onclick = function () {
    var person = people[current];
    person.age++; // Changing a bound property doesn't work yet ...
};
}

我们实施的生日按钮的 click 处理程序查找当前元素,并更改属性,具体的年龄。 然而,我们不是很在我们需要,是要得到的 UI 来自动更新。

问题是一个标准的 JavaScript 对象不支持任何通知协议通知有关各方 — — 如绑定 — — 其数据已更改。 若要添加通知协议是只需调用作为 WinJS.Binding 命名空间,从方法中所示图 6

图 6 添加通知协议

WinJS.UI.Pages.define("/pages/home/home.html", {
  ready: function (element, options) {
    var people = [
      // Notify binding listeners when these objects change
      WinJS.Binding.as({ name: "John", age: 18, favoriteColor: "red" }),
      WinJS.Binding.as({ name: "Tom", age: 16, favoriteColor: "green" }),
      WinJS.Binding.as({ name: "Chris", age: 42, favoriteColor: "blue" }),
    ];
    // Bind the current person to the HTML elements in the section
    var section = element.querySelector("section[role=main]");
    var current = 0;
    WinJS.Binding.processAll(section, people[current]);
    birthdayButton.onclick = function () {
      var person = people[current];
      person.age++; // Now this works!
};
  }
});

"作为"方法包装提供绑定通知协议,所以每个属性更改通知 (如我们绑定的 HTML 元素) 的任何绑定侦听器的每个对象。 与此代码中位置,改变一个人的年龄反映在用户界面中,作为图 7 显示。

Changing the Underlying Data Automatically Updates the Bound HTML Elements
图 7 的基础数据的更改会自动更新绑定的 HTML 元素

我们已经被一个单身人士对象绑定到的 HTML 元素的显示,集,但您可以处理具有一种不同的数据上下文的赢将数据绑定属性中的数据绑定表达式,如中所示图 8

图 8 数据绑定表达式的处理

WinJS.UI.Pages.define("/pages/home/home.html", {
  ready: function (element, options) {
    var people = [
      // Notify binding listeners when these objects change
      WinJS.Binding.as({ name: "John", age: 18, favoriteColor: "red" }),
      WinJS.Binding.as({ name: "Tom", age: 16, favoriteColor: "green" }),
      WinJS.Binding.as({ name: "Chris", age: 42, favoriteColor: "blue" }),
    ];
    // Bind the current person to the HTML elements in the section
    var section = element.querySelector("section[role=main]");
    var current = 0;
    var viewModel = WinJS.Binding.as({ person: people[current] });
    WinJS.Binding.processAll(section, viewModel);
    birthdayButton.onclick = function () {
      viewModel.person.age++;
    };
    // Bind to the previous object
    previousButton.onclick = function () {
      current = (people.length + current - 1) % people.length;
      viewModel.person = people[current];
    };
    // Bind to the next object
    nextButton.onclick = function () {
      current = (people.length + current + 1) % people.length;
      viewModel.person = people[current];
    };
  }
});

分组的数据,适合我们这种模式 (在本例中,若要查看当前的人员) 的"视图",这里是我们做之间从我们的应用程序"模式"(即,无事可做的本身显示的数据集) 中单一的数据传递的转变。 我们呼吁此变量"视图模式"后著名的和有用的技术执行调用模型-视图-ViewModel (MVVM) 的用户界面。 一旦我们已经建立了我们作为一个可绑定对象的视图模式,作为基础的属性更改 (在我们的例子中,该人士) 更新视图。 我们然后绑定我们认为到视图模型,以便在我们下一步和背面的按钮处理程序 — — 因为我们改变哪一个人我们想要查看 — — 通知视图。 为此,我们将需要更新的 HTML 视图模型 (而非直接使用,如中所示图 9

图 9 更新 html 代码,使用视图模型

 

<section aria-label="Main content" role="main">
  <!-- display each person -->
  <div id="nameLabel">Name</div>
  <input id="name" readonly="true" type="text" 
    data-win-bind="value: person.
name" />
  <div id="ageLabel">Age</div>
  <input id="age" readonly="true" type="text" 
    data-win-bind="value: person.age" />
  <div id="colorLabel">Favorite Color</div>
  <div id="color" 
    data-win-bind="style.backgroundColor: person.favoriteColor"></div>
  <div id="buttons">
    <button id="previousButton"></button>
    <button id="birthdayButton"></button>
    <button id="nextButton"></button>
  </div>
</section>

结果如图 10 所示。

You Can Rebind Different Objects to the Same Set of HTML Elements
图 10 你可以重新绑定到相同的 HTML 元素集的不同对象

除了将对象绑定到元素,绑定还允许您为只侦听要更改的值。 例如,现在我们更改索引的当前值在用户单击上一个或下一个按钮时,我们必须记住要编写代码来更改视图模型人字段,以匹配。 不过,您可以使用约束自己,在这里,帮助中所示图 11

图 11 后处理绑定表达式

WinJS.UI.Pages.define("/pages/home/home.html", {
  ready: function (element, options) {
    var people = [
      // Notify binding listeners when these objects change
      WinJS.Binding.as({ name: "John", age: 18, favoriteColor: "red" }),
      WinJS.Binding.as({ name: "Tom", age: 16, favoriteColor: "green" }),
      WinJS.Binding.as({ name: "Chris", age: 42, favoriteColor: "blue" }),
    ];
    // Bind the current person to the HTML elements in the section
    var section = element.querySelector("section[role=main]");
    // Listen for the current index to change and update the HTML
    var viewModel = WinJS.Binding.as({ current: 0, person: null });
    WinJS.Binding.processAll(section, viewModel);
    viewModel.bind("current", function (newValue, oldValue) {
      viewModel.person = people[newValue];
    });
    birthdayButton.onclick = function () {
      viewModel.person.age++;
    };
    // Bind to the previous object
    previousButton.onclick = function () {
      // Set the current index and let the binding do the work
      viewModel.current = (people.length + 
        viewModel.current - 1) % people.length;
    };
    // Bind to the next object
    nextButton.onclick = function () {
      // Set the current index and let the binding do the work
      viewModel.current = (people.length + 
        viewModel.current + 1) % people.length;
    };
  }
});

而不是简单的变量来保存索引的当前显示的人,我们向我们可绑定视图模型添加到当前正在查看的人的索引。 所有可绑定对象都有一个绑定方法,允许我们倾听对该对象的属性 (在此示例中的当前属性) 的更改。 当用户单击上一页或下一页按钮上时,我们只需更改索引的当前的人显示,让绑定处理程序过程的赢将数据绑定属性用于显示当前人的 HTML。

如果你想要停止侦听事件绑定,您可以调用解除绑定方法。

现在,我们提到的你看到的一切工作对默认值:单向绑定。 但是,如果您想要设置其他类型的绑定或参与绑定进程本身,你可以这样使用绑定初始值设定项。

初始值设定项

初始值设定项是可选的第三个参数指定函数调用时建立绑定绑定表达式:

<div data-win-bind="destProp: sourceProp init" ...></div>

如果您想要参加或甚至替换现有的约束力的行为作为绑定表达式的一部分提供一个初始值设定项 — — 例如,若要执行数据转换或甚至挂钩 jQuery 双向绑定。 绑定初始值设定项是一个函数,用于默认单向绑定接管并具有以下签名:

function myInit(source, sourceProperties,
  dest, destProperties) {...}

执行一个自定义的初始值设定项的详细信息超出范围的这篇文章,但几个初始值设定内置,其中您可以利用 WinJS 项。 例如,如果您想做一次性而不是单向的绑定,您可以使用 WinJS.Binding 命名空间中的一次性函数:

<input data-win-bind=
  "value: person.
name WinJS.Binding.oneTime"></input>

上另一端,执行单向绑定时,它通常是频谱的有用来执行数据转换 (也称为数据转换)。 例如,假设我们想要阐明我们生活中人的年龄:

function getWordsFromNumber(i) {...}
// Convert ages to words
window.ageToWords =
  WinJS.Binding.converter(function (value) { 
    return getWordsFromNumber(value); 
  });

转换器功能的 WinJS.Binding 命名空间提供了最困难的初始值设定项执行 ; 部分 你要做的就是提供一个函数来执行实际的转换,它将作为源值更改调用。 在 HTML 中使用它看起来像您期望:

<input data-win-bind="value: person.age ageToWords"></input>

您可以看到的结果图12

Translating a Numeric Age into Words via Data Conversion
图 12 将一个数字时代翻译成通过使用数据转换的单词

一次绑定值一是有用的但更有用的 — — 尤其是对 Windows 存储应用程序 — — 次全部绑定的值的集合。 为此我们有绑定列表。

绑定列表

想象的集合,这些值与使用 ListView 一个简单的示例:

<div data-win-control="WinJS.UI.ListView"
  data-win-options="{itemDataSource: items.dataSource}">
</div>

在这里我们要绑定到全局项对象的数据源属性的 HTML 中以声明方式创建 ListView 控件。 在 JavaScript 中,很容易创建和填充的项目对象的绑定列表:

// A global binding list of items
window.items = new WinJS.Binding.List();
[0, 1, 2].forEach(function (i) {
  WinJS.Promise.timeout(500 * (i+1)).done(function () {
    // Add an item to the binding list, updating the ListView
    items.push(i);
  });
});

在此代码中我们创建一个绑定列表 (WinJS.Binding 命名空间中的列表类型的实例) 和我们循环值的数组,使用每个创建的超时值,是在根据该参数在未来的一段时间,将触发一个承诺您传递 (毫秒)。 当每个超时完成后时,我们会看到作为获取额外的价值,ListView 图 13 显示。

The ListView Updating as the Underlying Binding List Updates
图 13 ListView 更新作为基础的绑定列表更新

HTML 不为 ID 列表视图,也不会以任何方式 ListView 与交互的 JavaScript。 相反,HTML 和 JavaScript 都交会绑定列表中的项目。 它是实现绑定 API (称为 IListDataSource) 的集合绑定列表。 而不是您对编程 IListDataSource 直接 — — 它不是特别是程序员友好 — — 绑定列表公开其 IListDataSource 执行通过数据源属性。

为了提供一个 API,是熟悉 JavaScript 的程序员,绑定列表实现类似的 API 和类似语义内置阵列的支持 — — 例如,推、 流行、 切片、 拼接等。 作为比较,在这里是内置的 JavaScript 数组的工作原理:

// Using the built-in array
var array = [];
array.push("one");
array.push("two");
array.push("three");
var x = array[0]; // x = "one"
var y = array[1]; // y = "two"
array[2] = "THREE";
var z = array[2]; // z = "THREE";

绑定列表行为同样,除外,而不是使用索引器 — — 也就是说,方括号 — — 它公开通过 setAt 和 getat) 的项目:

// Using the WinJS binding list
var list = new WinJS.Binding.List();
list.push("one");
list.push("two");
list.push("three");
var x = list.getAt(0); // x = "one"
var y = list.getAt(1); // y = "two"
list.setAt(2, "THREE");
var z = list.getAt(2); // z = "THREE";

排序和过滤

除了熟悉的 API 中,绑定列表建成的 Windows 应用程序商店建设的需要。 你已经看到了如何数据源属性允许您连接到 ListView 控件。 此外,您可能感兴趣的排序和筛选数据。 例如,在这里我们列出的人再次,这一次包裹在绑定列表中:

var people = new WinJS.Binding.List([
  { name: "Tom", age: 16 },
  { name: "John", age: 17 },
  { name: "Chris", age: 42 },
]);
// Sort by name
window.sortedPeople = people.
createSorted(function (lhs, rhs) { 
    return lhs.
name.localeCompare(rhs.
name); 
  });
// Filter by age (adults only)
window.filteredPeople = people.
createFiltered(function (p) { return p.age > 17; });

我们可以通过调用 createSorted 方法,传递排序函数,在绑定列表进行排序或筛选通过调用 createFiltered 方法,传递函数中进行筛选。 调用一个人的这些结果创建方法是视图数据上的外观和行为的绑定列表中,另一个实例一样,我们可以为绑定到列表视图, 图 14 显示。

Filtering a Binding List
图 14 筛选绑定列表

没有提供基础数据的副本,而是 createSorted 和 createFiltered 方法返回的实时视图的现有数据。 这意味着对基础数据的任何更改都反映在视图中,将在任何绑定控件中显示。 例如,我们可以将未成年人添加到基础列表:

// Changes to the underlying data are reflected in the live views
var person = { name: "Pete", age: 17, favoriteColor: "black" };
people.push(person);

即使我们改变基础数据、 将更新排序的视图和列表视图将作为通知的更改, 图 15 显示。

Sorting a Binding List
图 15 绑定列表排序

此外,因为从创建方法视图的外观和感觉就像一个绑定列表,可以进一步排序,或将堆叠方式筛选的视图:

// Filterd by age (minors) and sorted by name
window.filteredSortedPeople = people.
createFiltered(function (p) { return p.age < 18; }).
createSorted(function (lhs, rhs) { 
    return lhs.
name.localeCompare(rhs.
name); 
  });

绑定到由此产生的筛选和排序视图的结果不奇怪 (见图 16)。

Sorting and Filtering a Binding List
图 16 排序和筛选绑定列表

堆叠你这样做按正确的顺序时,一定要小心。 在筛选,如果进行排序或分组首先,然后筛选,你并做更多的工作,比你的。 每个视图层也开销,所以要确保你把它们堆只如必要深。

分组

排序和筛选数据绑定列表上,您还可以分组它的一些标准的数据。 例如,成年人和未成年人会工作以及对于我们的示例数据:

// Group by age
window.groupedPeople = people.
createGrouped(function (p) { return p.age < 18 ? "
minor" : "adult" });

分组函数的返回值是一个字符串值,用于唯一标识组 — — 在本例中,"小"的字符串或字符串"成人"。绑定到数据的分组视图显示按组排列的数据 — — 就是之前作为移动到下一组,每组中的所有项目图 17 显示。

Grouping a Binding List
图 17 分组绑定列表

此示例演示只是两个组,但您可以有任意数量的组。 此外,就像 createSorted 和 createFiltered,createGrouped 方法返回一个视图对实时数据,因此基础数据中的更改将反映在绑定的目的地。

然而,虽然很明显对我们来说,开发人员,我们要进行排序的群体 — — 第一大人,然后未成年人 — — 尚不清楚我们的程序的用户因为没有分组的可视指示器。 要问 ListView 提供可视化分组的一个指标,列表视图允许您指定两个数据集:分组的项目和自己的组的列表。 配置列表视图与这两组数据看起来像这样:

<div data-win-control="WinJS.UI.ListView"
  data-win-options="{itemDataSource: groupedPeople.dataSource,
                     groupDataSource: groupedPeople.groups.dataSource}">
</div>

一旦我们已经创建了绑定列表的分组的视图,组属性将公开这些组的列表,因此列表视图可以使用它们。 然而,我们能使它工作之前,我们还需要回到我们使用 createGrouped 函数:

// Group by age
function groupKeySelector(p) { return p.age < 18 ? "
minor" : "adult"; };
function groupDataSelector(p) { return p.age < 18 ? "
minor" : "adult"; };
window.groupedPeople = people.createGrouped(groupKeySelector, groupDataSelector);

以前,当我们被分组,我们仅仅需要提供一个函数,可以做基于一些独特组标识符的分组。 然而,如果我们想要实际显示的用户群体,我们需要可以提取这些团体自己的另一种方法 (如果您想要选择组的顺序,您可以提供第三种方法不会进行排序的 createGroup 方法)。 在这两种情况下,我们会提供一个字符串来表示就好,你可以看到在建立组,列表中的组以及图 18

Grouping a Binding List with ListView Headers
图 18 分组与列表视图的标题的绑定列表

作为图 18 显示,该集团实际上用来装饰分组的数据,使它很容易看到哪些数据属于哪一组。 不幸的是,即使有此分组的视觉指标,我们 JSON 格式的对象不真的是我们想要显示我们的用户的用户界面。 为此,我们希望的方式工作,需要的模板。

“报表服务器项目”

模板是可选的"孔"填写动态数据的 HTML 元素的单根层次结构。 Div 中的胜利将数据绑定属性定义使用 win 将数据绑定属性的孔。 例如,我们可以创建的模板我们人对象就像这样:

<div id="itemTemplate" data-win-control="WinJS.Binding.Template">
  <span data-win-bind="style.color: favoriteColor">
    <span data-win-bind="textContent: name"></span>
    <span> is </span>
    <span data-win-bind="textContent: age"></span>
    <span> years old</span>
  </span>
</div>

使此区块模板是 HTML 的将数据赢控件属性设置为 WinJS.Binding.Template 的控件类型。 一旦我们有我们的模板,您需要呈现以填充的孔与数据:

// Manual template rendering
var person = { name: "Pete", age: 17, favoriteColor: "black" };
var template = itemTemplate.winControl;
template.render(person).
done(function (element) { peteDiv.appendChild(element); });

因为模板是只是另一个控件 (虽然隐藏本身直到手动呈现的控件),我们可以深入我们的 div 定义它的增益访问其功能通过一个知名的属性称为 winControl。 我们想要访问的功能是模板的渲染方法,它采用约束力的胜利将数据绑定属性中使用的数据上下文,并生成一个完全成形的 HTML 元素,需要我们去做我们想要的。

如果您提供了一个模板列表视图,它将呈现该模板列表视图中的每个项目。 事实上,列表视图可以有用于呈现项和组标题的模板。 地看到,在采取行动,让我们首先更新组对象,如更常见的是:

// Fancy group by age
var groups = [{ key: 1, name: "Minor" }, { key: 2, name: "Adult" }];
function groupDataSelector(p) { 
  return p.age < 18 ?
groups[0] : groups[1]; 
};
function groupKeySelector(p) { return groupDataSelector(p).key; };
window.fancyGroupedPeople = 
  people.createGrouped(groupKeySelector, groupDataSelector);

在此代码中,我们有两组对象,每个键和一个名称,和我们有单独的数据和返回集团本身或组密钥,分别的键选择器函数。 作一点更真实世界像我们组数据,组页眉模板创建,如中所示图 19

图 19 组页眉模板

<div id="headerTemplate" data-win-control="WinJS.Binding.Template">
  <span data-win-bind="textContent: name"></span>
</div>
<div id="itemTemplate" data-win-control="WinJS.Binding.Template">
  <span data-win-bind="style.color: favoriteColor">
    <span data-win-bind="textContent: name"></span>
    <span> is </span>
    <span data-win-bind="textContent: age"></span>
    <span> years old</span>
  </span>
</div>
<h1>Fancy Grouped</h1>
<div data-win-control="WinJS.UI.ListView"
  data-win-options="{
    groupDataSource: fancyGroupedPeople.groups.dataSource,
    groupHeaderTemplate: select('#headerTemplate'),
    itemDataSource: fancyGroupedPeople.dataSource,
    itemTemplate: select('#itemTemplate')}">
</div>

就像一个项目模板创建的模板组页眉。 组页眉和项目模板是为 ListView 控件通过数据赢的选项属性中的 groupHeaderTemplate 和项模板的属性。 我们胡思乱想分组的数据看上去像图 20

Grouping a Binding List with ListView Headers and Templates
图 20 分组列表视图页眉和模板绑定列表

好吧,我们承认这不是很豪华,但组和项目,包括模板的组合显示绑定列表的电源。 事实上,绑定列表很有用,它是暴露从网格应用程序和拆分应用程序的项目模板,所生成的 data.js 文件,如中所示的异步数据模型的核心图 21

图 21 Data.js 文件生成的网格应用程序和拆分应用程序项目

// data.js
(function () {
  "use strict";
  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);
  });
  WinJS.Namespace.define("Data", {
    items: groupedItems,
    groups: groupedItems.groups,
    getItemsFromGroup: getItemsFromGroup,
    ...
});
  ...
// This function returns a WinJS.Binding.List containing only the items
  // that belong to the provided group.
function getItemsFromGroup(group) {
    return list.createFiltered(function (item) {
    return item.group.key === group.key; });
  }
  ...
});
})();

您可以看到空绑定列表,创建该列表中,使用做关键的功能和数据的选择对这些团体的分组版本的创建、 添加的项的绑定列表和最后,帮助器函数的小 forEach 循环不会过滤。

我们在哪?

我们开始这篇文章通过挖掘到绑定对象的属性关联的 HTML 元素中的属性的能力。 现成的 WinJS 支持单向、 一次性和自定义绑定初始值设定项,但不是双向绑定。 绑定支持单个可绑定的对象,以及实现 IListDataSource 接口的对象列表。 最容易的地方,要获取 IListDataSource 接口的实现是通过绑定列表 (WinJS.Binding.List) 对象,它完全支持排序、 筛选和分组。 我们也看到了如何有用的模板时,组合与绑定和如何有用的模板和绑定是支持列表绑定,如列表视图控件的时候。

克里斯出售 是在 Telerik 的开发者工具司的副总裁。他是"建筑 Windows 8 的应用程序与 JavaScript"(艾迪生 - 韦斯利专业、 2012年)、 中国知识产权报从这篇文章被改编的。sellsbrothers.com 上提供了有关 Sells 和他的各种项目的详细信息。

布兰登 Satrom 是一个程序管理器在剑道 UI 司在 Telerik.他是"建筑 Windows 8 的应用程序与 JavaScript"(艾迪生 - 韦斯利专业、 2012年)、 中国知识产权报从这篇文章被改编的。您可以按照他在 Twitter 上 twitter.com/BrandonSatrom

衷心感谢以下技术专家对本文的审阅:克里斯 · 安德森、 乔纳森 · 安托万、 迈克尔 · 魏因哈特、 肖恩 · 维尔德穆特和乔什 · 威廉斯