第八章:简单应用举例(2)
5 Control types
这个例子,对view model没有什么特殊的展示,只是展示如何绑定到各种元素上(例如,select, radio button等)。
<div class="readout"> <h3> What's in the model?</h3> <table> <tr> <td class="label"> Text value: </td> <td data-bind="text: stringValue"> </td> </tr> <tr> <td class="label"> Password: </td> <td data-bind="text: passwordValue"> </td> </tr> <tr> <td class="label"> Bool value: </td> <td data-bind='text: booleanValue() ? "True" : "False"'> </td> </tr> <tr> <td class="label"> Selected option: </td> <td data-bind="text: selectedOptionValue"> </td> </tr> <tr> <td class="label"> Multi-selected options: </td> <td data-bind="text: multipleSelectedOptionValues"> </td> </tr> <tr> <td class="label"> Radio button selection: </td> <td data-bind="text: radioSelectedOptionValue"> </td> </tr> </table> </div> <h3> HTML controls</h3> <table> <tr> <td class="label"> Text value (updates on change): </td> <td> <input data-bind="value: stringValue"/> </td> </tr> <tr> <td class="label"> Text value (updates on keystroke): </td> <td> <input data-bind='value: stringValue, valueUpdate: "afterkeydown"' /> </td> </tr> <tr> <td class="label"> Text value (multi-line): </td> <td> <textarea data-bind="value: stringValue"> </textarea> </td> </tr> <tr> <td class="label"> Password: </td> <td> <input type="password" data-bind="value: passwordValue"/> </td> </tr> <tr> <td class="label"> Checkbox: </td> <td> <input type="checkbox" data-bind="checked: booleanValue"/> </td> </tr> <tr> <td class="label"> Drop-down list: </td> <td> <select data-bind="options: optionValues, value: selectedOptionValue"> </select> </td> </tr> <tr> <td class="label"> Multi-select drop-down list: </td> <td> <select multiple="multiple" data-bind="options: optionValues, selectedOptions: multipleSelectedOptionValues"> </select> </td> </tr> <tr> <td class="label"> Radio buttons: </td> <td> <label> <input type="radio" value="Alpha" data-bind="checked: radioSelectedOptionValue"/>Alpha</label> <label> <input type="radio" value="Beta" data-bind="checked: radioSelectedOptionValue"/>Beta</label> <label> <input type="radio" value="Gamma" data-bind="checked: radioSelectedOptionValue"/>Gamma</label> </td> </tr> </table>
代码: View model
var viewModel = { stringValue: ko.observable("Hello"), passwordValue: ko.observable("mypass"), booleanValue: ko.observable(true), optionValues: ["Alpha", "Beta", "Gamma"], selectedOptionValue: ko.observable("Gamma"), multipleSelectedOptionValues: ko.observable(["Alpha"]), radioSelectedOptionValue: ko.observable("Beta") }; ko.applyBindings(viewModel);
6 Templating
这个例子展示的render模板,以及在模板内部如何使用data binding属性的。
Template很容易嵌套,当任何依赖数据改变的时候,Knockout会自动重新render模板。参考演示(启用‘Show render times’),Knockout知道只需要重新render改变的那些数据所绑定的最近的模板。
代码: View
<div data-bind='template: "peopleTemplate"'> </div> <label> <input type="checkbox" data-bind="checked: showRenderTimes"/> Show render times</label> <script type="text/html" id="peopleTemplate"> <h2>People</h2> <ul> {{each people}} <li> <div> ${ name } has <span data-bind="text: children().length"> </span> children: <a href="#" data-bind="click: addChild ">Add child</a> <span class="renderTime" data-bind="visible: showRenderTimes"> (person rendered at <span data-bind="text: new Date().getSeconds()"></span>) </span> </div> <div data-bind='template: { name: "childrenTemplate", data: children }'></div> </li> {{/each}} </ul> </script> <script type="text/html" id="childrenTemplate"> <ul> {{each $data}} <li> ${ this } <span class="renderTime" data-bind="visible: viewModel.showRenderTimes"> (child rendered at <span data-bind="text: new Date().getSeconds()"></span>) </span> </li> {{/each}} </ul> </script> 代码: View model 隐藏代码 // Define a "person" class that tracks its own name and children, and has a method to add a new child var person = function (name, children) { this.name = name; this.children = ko.observableArray(children); this.addChild = function () { this.children.push("New child"); } .bind(this); } // The view model is an abstract description of the state of the UI, but without any knowledge of the UI technology (HTML) var viewModel = { people: [ new person("Annabelle", ["Arnie", "Anders", "Apple"]), new person("Bertie", ["Boutros-Boutros", "Brianna", "Barbie", "Bee-bop"]), new person("Charles", ["Cayenne", "Cleopatra"]) ], showRenderTimes: ko.observable(false) }; ko.applyBindings(viewModel);
7 Paged grid
data-bind="..."绑定(像text, visible, 和click不是固定死的) - 你可以很容易自定义自己的绑定。如果你的自定义绑定仅仅是添加事件或者更新DOM元素的属性,那几行就可以实现。不过,你依然可以自定义可以重用的绑定 (或插件),就行本例的simpleGrid绑定。
如果一个插件需要自己标准的view model(例如本例的ko.simpleGrid.viewModel ),它提供既提供了该如何配置插件实例(分页大小,列声明)工作,也提供了view model上的属性是否是observable 的(例如currentpage索引)。也可以扩展代码让这些属性很容易地改变,并且让UI自动更新。例如,“Jump to first page”按钮的工作原理。
查看HTML源代码可以看到非常容易使用这个simple grid插件。simpleGrid源码地址是:http://knockoutjs.com/examples/resources/knockout.simpleGrid.js
<div data-bind="simpleGrid: gridViewModel"> </div> <button data-bind='click: function() { items.push({ name: "New item", sales: 0, price: 100 }) }'> Add item </button> <button data-bind="click: sortByName"> Sort by name </button> <button data-bind="click: function() { gridViewModel.currentPageIndex(0) }"> Jump to first page </button>
代码: View model
var myModel = { items: ko.observableArray([ { name: "Well-Travelled Kitten", sales: 352, price: 75.95 }, { name: "Speedy Coyote", sales: 89, price: 190.00 }, { name: "Furious Lizard", sales: 152, price: 25.00 }, { name: "Indifferent Monkey", sales: 1, price: 99.95 }, { name: "Brooding Dragon", sales: 0, price: 6350 }, { name: "Ingenious Tadpole", sales: 39450, price: 0.35 }, { name: "Optimistic Snail", sales: 420, price: 1.50 } ]), sortByName: function () { this.items.sort(function (a, b) { return a.name < b.name ? -1 : 1; }); } }; myModel.gridViewModel = new ko.simpleGrid.viewModel({ data: myModel.items, columns: [ { headerText: "Item Name", rowText: "name" }, { headerText: "Sales Count", rowText: "sales" }, { headerText: "Price", rowText: function (item) { return "$" + item.price.toFixed(2) } } ], pageSize: 4 }); ko.applyBindings(myModel);
8 Animated transitions
该例子展示了2种方式实现动画过渡效果:
当使用 template/foreach绑定的时候,你可以使用afterAdd和beforeRemove回调函数,他们可以让你写代码真实操作添加和删除元 素,这样你就可以使用像jQuery的 slideUp/slideDown()这样的动画效果。在planet types之间切换或添加新的planet可以看到效果。
通过observable 类型的值,我们不难定义自己的Knockout绑定,查看HTML源代码可以看到一个自定义绑定fadeVisible,不管什么时候它改变了, jQuery就会在相关的元素上执行fadeIn/fadeOut动画效果。点击“advanced options” checkbox 可以看到效果。
v
<h2>Planets</h2> <p> <label> <input type="checkbox" data-bind="checked: displayAdvancedOptions"/> Display advanced options </label> </p> <p data-bind="fadeVisible: displayAdvancedOptions"> Show: <label><input type="radio" value="all" data-bind="checked: typeToShow"/>All</label> <label><input type="radio" value="rock" data-bind="checked: typeToShow"/>Rocky planets</label> <label><input type="radio" value="gasgiant" data-bind="checked: typeToShow"/>Gas giants</label> </p> <div data-bind='template: { name: "planetsTemplate", foreach: planetsToShow, beforeRemove: function(elem) { $(elem).slideUp(function() { $(elem).remove(); }) }, afterAdd: function(elem) { $(elem).hide().slideDown() } }'> </div> <script type="text/html" id="planetsTemplate"> <div class="planet ${ type }">${ name }</div> </script> <p data-bind="fadeVisible: displayAdvancedOptions"> <button data-bind='click: function() { addPlanet("rock") }'> Add rocky planet</button> <button data-bind='click: function() { addPlanet("gasgiant") }'> Add gas giant</button> </p>
代码: View model
var viewModel = { planets: ko.observableArray([ { name: "Mercury", type: "rock" }, { name: "Venus", type: "rock" }, { name: "Earth", type: "rock" }, { name: "Mars", type: "rock" }, { name: "Jupiter", type: "gasgiant" }, { name: "Saturn", type: "gasgiant" }, { name: "Uranus", type: "gasgiant" }, { name: "Neptune", type: "gasgiant" }, { name: "Pluto", type: "rock" } ]), typeToShow: ko.observable("all"), displayAdvancedOptions: ko.observable(false), addPlanet: function (type) { this.planets.push({ name: "New planet", type: type }); } }; viewModel.planetsToShow = ko.dependentObservable(function () { // Represents a filtered list of planets // i.e., only those matching the "typeToShow" condition var desiredType = this.typeToShow(); if (desiredType == "all") return this.planets(); return ko.utils.arrayFilter(this.planets(), function (planet) { return planet.type == desiredType; }); } .bind(viewModel)); // Here's a custom Knockout binding that makes elements shown/hidden via jQuery's fadeIn()/fadeOut() methods // Could be stored in a separate utility library ko.bindingHandlers.fadeVisible = { init: function (element, valueAccessor) { // Initially set the element to be instantly visible/hidden depending on the value var value = valueAccessor(); $(element).toggle(ko.utils.unwrapObservable(value)); // Use "unwrapObservable" so we can handle values that may or may not be observable }, update: function (element, valueAccessor) { // Whenever the value subsequently changes, slowly fade the element in or out var value = valueAccessor(); ko.utils.unwrapObservable(value) ? $(element).fadeIn() : $(element).fadeOut(); } }; ko.applyBindings(viewModel);