Knockout应用开发指南

第一章:入门javascript

一、Knockout简介 (Introduction)css

Knockout是一个轻量级的UI类库,经过应用MVVM模式使JavaScript前端UI简单化。html

Knockout有以下4大重要概念:前端

声明式绑定 (Declarative Bindings):使用简明易读的语法很容易地将模型(model)数据关联到DOM元素上。java

UI界面自动刷新 (Automatic UI Refresh):当您的模型状态(model state)改变时,您的UI界面将自动更新。node

依赖跟踪 (Dependency Tracking):为转变和联合数据,在你的模型数据之间隐式创建关系。jquery

模板 (Templating):为您的模型数据快速编写复杂的可嵌套的UI。git

简称:KOgithub

官方网站:http://knockoutjs.comweb

二、入门介绍 (Getting started)

2.1KO工做原理及带来的好处

Knockout是一个以数据模型(data model)为基础的可以帮助你建立富文本,响应显示和编辑用户界面的JavaScript类库。任什么时候候若是你的UI须要自动更新(好比:更新依赖于用 户的行为或者外部数据源的改变),KO可以很简单的帮你实现而且很容易维护。

重要特性:

优雅的依赖追踪- 无论任什么时候候你的数据模型更新,都会自动更新相应的内容。

声明式绑定- 浅显易懂的方式将你的用户界面指定部分关联到你的数据模型上。

灵活全面的模板- 使用嵌套模板能够构建复杂的动态界面。

轻易可扩展- 几行代码就能够实现自定义行为做为新的声明式绑定。

额外的好处:

纯JavaScript类库 – 兼容任何服务器端和客户端技术

可添加到Web程序最上部 – 不须要大的架构改变

简洁的 – Gzip以前大约25kb

兼容任何主流浏览器 (IE 6+、Firefox 2+、Chrome、Safari、其它)

Comprehensive suite of specifications (采用行为驱动开发) - 意味着在新的浏览器和平台上能够很容易经过验证。

开发人员若是用过Silverlight或者WPF可能会知道KO是MVVM模式的一个例子。若是熟悉 Ruby on Rails 或其它MVC技术可能会发现它是一个带有声明式语法的MVC实时form。换句话说,你能够把KO当成经过编辑JSON数据来制做UI用户界面的一种方 式… 无论它为你作什么

OK, 如何使用它?

简单来讲:声明你的数据做为一个JavaScript 模型对象(model object),而后将DOM 元素或者模板(templates)绑定到它上面.

让咱们来看一个例子

想一想在一个页面上,航空旅客能够为他们的旅行升级高级食物套餐,当他们选择一个套餐的时候,页面当即显示套餐的描述和价格。首先,声明可用的套餐:

var availableMeals = [
    { mealName: 'Standard', description: 'Dry crusts of bread', extraCost: 0 },
    { mealName: 'Premium', description: 'Fresh bread with cheese', extraCost: 9.95 },
    { mealName: 'Deluxe', description: 'Caviar and vintage Dr Pepper', extraCost: 18.50 }
  ];

若是想让这3个选项显示到页面上,咱们能够绑定一个下拉菜单(例如:<select>元素)到这个数据上。例如:

<h3>Meal upgrades</h3>
<p>Make your flight more bearable by selecting a meal to match your social and economic status.</p>
Chosen meal: <select data-bind="options: availableMeals,
                                optionsText: 'mealName'"></select>

启用Knockout并使你的绑定生效,在availableMeals变量声明以后添加以下代码:

var viewModel = {
    /* we'll populate this in a moment */
};

ko.applyBindings(viewModel); // Makes Knockout get to work
// 注意:ko. applyBindings须要在上述HTML以后应用才有效

你能够在这个系列里将学习更多的view model 和 MVVM。如今你的页面将render成以下的样子:

 

 

响应选择

下一步,声明一个简单的data model来描述旅客已经选择的套餐,添加一个属性到当前的view model上:

var viewModel = {
    chosenMeal: ko.observable(availableMeals[0])
};

ko.observable是什么?它是KO里的一个基础概念。UI能够监控(observe)它的值而且回应它的变化。这里咱们设置chosenMeal是UI能够监控已经选择的套餐,并初始化它,使用availableMeal里的第一个值做为它的默认值(例如:Standard)。

让咱们将chosenMeal 关联到下拉菜单上,仅仅是更新<select>的data-bind属性,告诉它让<select>元素的值读取/写入chosenMeal这个模型属性:

Chosen meal: <select data-bind="options: availableMeals,
                                optionsText: 'mealName',
                                value: chosenMeal"></select>

理论上说,咱们如今能够读/写chosenMeal 属性了,可是咱们不知道它的做用。让咱们来显示已选择套餐的描述和价格:

<p>
    You've chosen:
    <b data-bind="text: chosenMeal().description"></b>
    (price: <span data-bind='text: chosenMeal().extraCost'></span>)
</p>

因而,套餐信息和价格,将根据用户选择不一样的套餐项而自动更新:

 

更多关于observables和dependency tracking的使用

最后一件事:若是能将价格格式化成带有货币符号的就行了,声明一个JavaScript函数就能够实现了…

function formatPrice(price) {
    return price == 0 ? "Free" : "$" + price.toFixed(2);
}

… 而后更新绑定信息使用这个函数 …

(price: <span data-bind='text: formatPrice(chosenMeal().extraCost)'></span>)

… 界面显示结果将变得好看多了:

 

Price的格式化展现了,你能够在你的绑定里写任何JavaScript代码,KO仍然能探测到你的绑定依赖代码。这就展现了当你的model改变时,KO如何只进行局部更新而不用从新render整个页面 – 仅仅是有依赖值改变的那部分。

 

链式的observables也是支持的(例如:总价依赖于价格和数量)。当链改变的时候,依赖的下游部分将会从新执行,同时全部相关的UI将自动更新。不须要在各个observables之间声明关联关系,KO框架会在运行时自动执行的。

你能够从 observables 和 observable arrays 获取更多信息。上面的例子很是简单,没有覆盖不少KO的功能。你能够获取更多的内嵌的绑定和模板绑定。

 

KO和jQuery (或Prototype等)是竞争关系仍是能一块儿使用?

全部人都喜欢jQuery! 它是一个在页面里操做元素和事件的框架,很是出色而且易使用,在DOM操做上确定使用jQuery,KO解决不一样的问题。

若是页面要求复杂,仅仅使用jQuery须要花费更多的代码。 例如:一个表格里显示一个列表,而后统计列表的数量,Add按钮在数据行TR小于5调的时候启用,不然就禁用。jQuery 没有基本的数据模型的概念,因此须要获取数据的数量(从table/div或者专门定义的CSS class),若是须要在某些SPAN里显示数据的数量,当添加新数据的时候,你还要记得更新这个SPAN的text。固然,你还要判断当总数>=5条的时候禁用Add按钮。 而后,若是还要实现Delete功能的时候,你不得不指出哪个DOM元素被点击之后须要改变。

 

Knockout的实现有何不一样?

使用KO很是简单。将你的数据描绘成一个JavaScript数组对象myItems,而后使用模板(template)转化这个数组到表格里(或者一组DIV)。无论何时数组改变, UI界面也会响应改变(不用指出如何插入新行<tr>或在哪里插入),剩余的工做就是同步了。例如:你能够声明绑定以下一个SPAN显示数据数量(能够放在页面的任何地方,不必定非要在template里):

There are <span data-bind="text: myItems().count"></span> items

就是这些!你不须要写代码去更新它,它的更新依赖于数组myItems的改变。一样, Add按钮的启用和禁用依赖于数组myItems的长度,以下:

<button data-bind="enable: myItems().count < 5">Add</button>

以后,若是你要实现Delete功能,没必要指出如何操做UI元素,只须要修改数据模型就能够了。

 

总结:KO没有和jQuery或相似的DOM 操做API对抗竞争。KO提供了一个关联数据模型和用户界面的高级功能。KO自己不依赖jQuery,可是你能够一块儿同时使用jQuery, 生动平缓的UI改变须要真正使用jQuery。

2.2下载安装

Knockout的核心类库是纯JavaScript代码,不依赖任何第三方的类库。因此按照以下步骤便可添加KO到你的项目里:

下载Knockout类库的最新版本,在正式开发和产品使用中,请使用默认的压缩版本(knockout-x.x.js)。
下载地址:https://github.com/SteveSanderson/knockout/downloads

Debug调试目的,可以使用非压缩版本(knockout-x.x.debug.js). 和压缩版本一样的功能,可是具备全变量名和注释的可读性源代码,而且没有隐藏内部的API。

在你的HTML页面里使用<script>标签引用Knockout类库文件。

这就是你须要的一切…

开启模板绑定

…除非你想使用模板绑定功能(您颇有可能使用它,由于很是有用),那你须要再引用两个JavaScript文件。 KO1.3版的默认模板引擎是依赖jQuery 的jquery.tmpl.js(最新版2.0版已经不依赖jquery tmp了)。因此你须要下载下面的2个文件并在引用KO以前引用:

jQuery 1.4.2 或更高版本

jquery-tmpl.js — 此版本 能够很容易使用,或者你访问官方网站 查找最新版本。

正确的引用顺序:

<script type='text/javascript' src='jquery-1.4.2.min.js'></script>
<script type='text/javascript' src='jquery-tmpl.js'></script>
<script type='text/javascript' src='knockout-1.2.1.js'></script>

(固然,您要根据你的文件路径累更新上面的文件路径和文件名。)

Knockout应用开发指南 第二章:监控属性(Observables)

关于Knockout的3个重要概念(Observables,DependentObservables,ObservableArray),本人没法准确表达它的准确含义,因此暂定翻译为(监控属性、依赖监控属性和监控数组),若是有好的建议请指正,多谢。

一、建立带有监控属性的view model

Observables

Knockout是在下面三个核心功能是创建起来的:

监控属性(Observables)和依赖跟踪(Dependency tracking)

声明式绑定(Declarative bindings)

模板(Templating)

 这一节,你讲学到3个功能中的第一个。 在这以前, 咱们来解释一下MVVM模式和view model的概念。

 MVVM and View Models

Model-View-View Model (MVVM) 是一种建立用户界面的设计模式。 描述的是如何将复杂的UI用户界面分红3个部分:

 model: 你程序里存储的数据。这个数据包括对象和业务操做(例如:银子帐户能够完成转帐功能), 而且独立于任何UI。使用KO的时候,一般说是向服务器调用Ajax读写这个存储的模型数据。

 view model: 在UI上,纯code描述的数据以及操做。例如,若是你实现列表编辑,你的view model应该是一个包含列表项items的对象和暴露的add/remove列表项(item)的操做方法。

    注意这不是UI自己:它不包含任何按钮的概念或者显示风格。它也不是持续数据模型 – 包含用户正在使用的未保存数据。使用KO的时候,你的view models是不包含任何HTML知识的纯JavaScript 对象。保持view model抽象能够保持简单,以便你能管理更复杂的行为。

 view: 一个可见的,交互式的,表示view model状态的UI。 从view model显示数据,发送命令到view model(例如:当用户click按钮的时候) ,任何view model状态改变的时候更新。

使用KO的时候,你的view就是你带有绑定信息的HTML文档,这些声明式的绑定管理到你的view model上。或者你可使用模板从你的view model获取数据生成HTML。

建立一个view model,只须要声明任意的JavaScript object。例如:

var myViewModel = {
    personName: 'Bob',
    personAge: 123
};

你能够为view model建立一个声明式绑定的简单view。例如:下面的代码显示personName 值:

The name is <span data-bind="text: personName"></span>

 

Activating Knockout

data-bind属性尽快好用但它不是HTML的原生属性(它严格听从HTML5语法, 虽然HTML4验证器提示有不可识别的属性但依然可用)。因为浏览器不识别它是什么意思,因此你须要激活Knockout 来让他起做用。

激活Knockout,须要添加以下的 <script> 代码块:

ko.applyBindings(myViewModel);

你能够将这个代码块放在HTML底部,或者放在jQuery的$函数或者ready 函数里,而后放在页面上面, 最终生成结果就是以下的HTML代码:

The name is <span>Bob</span>

你可能奇怪ko.applyBindings使用的是什么样的参数,

 第一个参数是你想用于声明式绑定

第二个参数(可选),能够声明成使用data-bind的HTML元素或者容器。例如, ko.applyBindings(myViewModel, document.getElementById('someElementId'))。它的如今是只有做为someElementId 的元素和子元素才能激活KO功能。 好处是你能够在同一个页面声明多个view model,用来区分区域。

 

Observables

如今已经知道如何建立一个简单的view model而且经过binding显示它的属性了。可是KO一个重要的功能是当你的view model改变的时候能自动更新你的界面。当你的view model部分改变的时候KO是如何知道的呢?答案是:你须要将你的model属性声明成observable的, 由于它是很是特殊的JavaScript objects,可以通知订阅者它的改变以及自动探测到相关的依赖。

 

例如:将上述例子的view model改为以下代码:

var myViewModel = {
    personName: ko.observable('Bob'),
    personAge: ko.observable(123)
};

你根本不须要修改view – 全部的data-bind语法依然工做,不一样的是他能监控到变化,当值改变时,view会自动更新。

 

监控属性(observables)的读和写

不是全部的浏览器都支持JavaScript的 getters and setters (好比IE),,因此为了兼容性,使用ko.observable监控的对象都是真实的function函数。

 读取监控属性(observable)的值,只须要直接调用监控属性(observable)(不须要参数),例如myViewModel.personName() 将返回'Bob', myViewModel.personAge() 将返回 123。

写一个新值到监控属性(observable)上,调用这个observable属性并当新值做为参数。例如:调用 myViewModel.personName('Mary') 将更新name值为'Mary'。

给一个model对象的多个属性写入新值,你可使用链式语法。例如: myViewModel.personName('Mary').personAge(50) 将会将name更新为 'Mary' 而且 将age更新为 50.

监控属性(observables)的特征就是监控(observed),例如其它代码能够说我须要获得对象变化的通知,因此KO内部有不少内置的绑定语法。因此若是你的代码写成data-bind="text: personName", text绑定注册到自身,一旦personName的值改变,它就能获得通知。

固然调用myViewModel.personName('Mary')改变name的值,text绑定将自动更新这个新值到相应的DOM元素上。这就是如何将view model的改变传播到view上的。

 

监控属性(Observables)的显式订阅

一般状况下,你不用手工订阅,因此新手能够忽略此小节。高级用户,若是你要注册本身的订阅到监控属性(observables),你能够调用它的subscribe 函数。例如:

myViewModel.personName.subscribe(function (newValue) {
    alert("The person's new name is " + newValue);
});

这个subscribe 函数在内部不少地方都用到的。你也能够终止本身的订阅:首先获得你的订阅,而后调用这个对象的dispose函数,例如:

var subscription = myViewModel.personName.subscribe(function (newValue) { /* do stuff */ });
// ...then later...
subscription.dispose(); // I no longer want notifications

大多数状况下,你不须要作这些,由于内置的绑定和模板系统已经帮你作好不少事情了,能够直接使用它们。

 

二、使用依赖监控属性(Dependent Observables)

若是你已经有了监控属性firstName和lastName,你想显示全称怎么办? 这就须要用到依赖监控属性了 – 这些函数是一个或多个监控属性, 若是他们的依赖对象改变,他们会自动跟着改变。

例如,下面的view model,

var viewModel = {
    firstName: ko.observable('Bob'),
    lastName: ko.observable('Smith')
};

… 你能够添加一个依赖监控属性来返回姓名全称:

viewModel.fullName = ko.dependentObservable(function () {
    return this.firstName() + " " + this.lastName();
}, viewModel);

而且绑定到UI的元素上,例如:

The name is <span data-bind="text: fullName"></span>

… 无论firstName仍是lastName改变,全称fullName都会自动更新(无论谁改变,执行函数都会调用一次,无论改变成什么,他的值都会更新到UI或者其余依赖监控属性上)。

 

管理‘this’

新手可忽略此小节,你只须要安装上面例子中的代码模式写就好了,无需知道/关注这个this。

你可能疑惑ko.dependentObservable的第二个参数是作什么用的(上面的例子中我传的是viewModel), 它是声明执行依赖监控属性的this用的。 没有它,你不能引用到this.firstName() 和this.lastName()。 老练的JavaScript 开发人员不以为this怎么样,可是若是你不熟悉JavaScript,那就对它就会很陌生。(C#和Java须要不须要为set一个值为设置this,可是JavaScript 须要,由于默认状况下他们的函数自身不是任何对象的一部分)。

 

不幸的是, JavaScript 对象没有任何办法能引用他们自身,因此你须要经过myViewModelObject.myDependentObservable = ... 的形式添加依赖监控属性到view model对象上。 你不能直接在view model里声明他们,换句话说,你不能写成下面这样:

var viewModel = {
    myDependentObservable: ko.dependentObservable(function() {
        ...
    }, /* can't refer to viewModel from here, so this doesn't work */)
}

… 相反你必须写成以下这样:

var viewModel = {
    // Add other properties here as you wish
};

viewModel.myDependentObservable = ko.dependentObservable(function() {
    ...
}, viewModel); // This is OK

只要你知道指望什么,它确实不是个问题。J

 

依赖链

理所固然,若是你想你能够建立一个依赖监控属性的链。例如:

监控属性items表述一组列表项

监控属性selectedIndexes保存着被用户选上的列表项的索引

依赖监控属性selectedItems 返回的是selectedIndexes 对应的列表项数组

另外一个依赖监控属性返回的true或false依赖于 selectedItems 的各个列表项是否包含一些属性(例如,是否新的或者还未保存的)。一些UI element(像按钮的启用/禁用)的状态取决于这个值)。

 而后,items或者selectedIndexes 的改变将会影响到全部依赖监控属性的链,全部绑定这些属性的UI元素都会自动更新。多么整齐与优雅!

 

可写的依赖监控属性

新手可忽略此小节,可写依赖监控属性真的是太advanced了,并且大部分状况下都用不到。

正如所学到的,依赖监控属性是经过计算其它的监控属性而获得的。感受是依赖监控属性正常状况下应该是只读的。那么,有可能让依赖监控属性支持可写么?你只须要声明本身的callback函数而后利用写入的值再处理一下相应的逻辑便可。

你能够像使用普通的监控属性同样使用依赖监控属性 – 数据双向绑定到DOM元素上,而且经过自定义的逻辑拦截全部的读和写操做。这是很是牛逼的特性而且能够在大范围内使用。

 

例1:分解用户的输入

返回到经典的“first name + last name = full name” 例子上,你可让事情调回来看: 让依赖监控属性fullName可写,让用户直接输入姓名全称,而后输入的值将被解析并映射写入到基本的监控属性firstName和lastName上:

var viewModel = {
    firstName: ko.observable("Planet"),
    lastName: ko.observable("Earth")
};

viewModel.fullName = ko.dependentObservable({

    read: function () {
        return this.firstName() + " " + this.lastName();
    },

    write: function (value) {
        var lastSpacePos = value.lastIndexOf(" ");
        if (lastSpacePos > 0) { // Ignore values with no space character
            this.firstName(value.substring(0, lastSpacePos)); // Update "firstName"
            this.lastName(value.substring(lastSpacePos + 1)); // Update "lastName"
        }
    },
    owner: viewModel
});

这个例子里,写操做的callback接受写入的值,把值分离出来,分别写入到“firstName”和“lastName”上。 你能够像普通状况同样将这个view model绑定到DOM元素上,以下:

<p>First name: <span data-bind="text: firstName"></span></p>
<p>Last name: <span data-bind="text: lastName"></span></p>
<h2>Hello, <input data-bind="value: fullName"/>!</h2>

这是一个Hello World 例子的反例子,姓和名都不可编辑,相反姓和名组成的姓名全称倒是可编辑的。

上面的view model演示的是经过一个简单的参数来初始化依赖监控属性。你能够给下面的属性传入任何JavaScript对象:

   read — 必选,一个用来执行取得依赖监控属性当前值的函数。

   write — 可选,若是声明将使你的依赖监控属性可写,别的代码若是这个可写功能写入新值,经过自定义逻辑将值再写入各个基础的监控属性上。

   owner — 可选,若是声明,它就是KO调用read或write的callback时用到的this。查看“管理this”获取更新信息。

 例2:Value转换器

 有时候你可能须要显示一些不一样格式的数据,从基础的数据转化成显示格式。好比,你存储价格为float类型,可是容许用户编辑的字段须要支持货币单位和小数点。你能够用可写的依赖监控属性来实现,而后解析传入的数据到基本 float类型里:

viewModel.formattedPrice = ko.dependentObservable({

    read: function () {
        return "$" + this.price().toFixed(2);
    },

    write: function (value) {
        // Strip out unwanted characters, parse as float, then write the raw data back to the underlying "price" observable
        value = parseFloat(value.replace(/[^\.\d]/g, ""));
        this.price(isNaN(value) ? 0 : value); // Write to underlying storage
    },
    owner: viewModel
});

而后咱们绑定formattedPrice到text box上:

<p>Enter bid price: <input data-bind="value: formattedPrice"/></p>

因此,无论用户何时输入新价格,输入什么格式,text box里会自动更新为带有2位小数点和货币符号的数值。这样用户能够看到你的程序有多聪明,来告诉用户只能输入2位小数,不然的话自动删除多余的位数,固然也不能输入负数,由于write的callback函数会自动删除负号。

 

例3:过滤并验证用户输入

例1展现的是写操做过滤的功能,若是你写的值不符合条件的话将不会被写入,忽略全部不包括空格的值。

再多走一步,你能够声明一个监控属性isValid 来表示最后一次写入是否合法,而后根据真假值显示相应的提示信息。稍后仔细介绍,先参考以下代码:

var viewModel = {
    acceptedNumericValue: ko.observable(123),
    lastInputWasValid: ko.observable(true)
};

viewModel.attemptedValue = ko.dependentObservable({
    read: viewModel.acceptedNumericValue,
    write: function (value) {
        if (isNaN(value))
            this.lastInputWasValid(false);
        else {
            this.lastInputWasValid(true);
            this.acceptedNumericValue(value); // Write to underlying storage
        }
    },
    owner: viewModel
});

… 按照以下格式声明绑定元素:

<p>Enter a numeric value: <input data-bind="value: attemptedValue"/></p>
<div data-bind="visible: !lastInputWasValid()">That's not a number!</div>

如今,acceptedNumericValue 将只接受数字,其它任何输入的值都会触发显示验证信息,而会更新acceptedNumericValue。

备注:上面的例子显得杀伤力太强了,更简单的方式是在<input>上使用jQuery Validation和number class。Knockout能够和jQuery Validation一块儿很好的使用,参考例子:grid editor 。固然,上面的例子依然展现了一个如何使用自定义逻辑进行过滤和验证数据,若是验证很复杂而jQuery Validation很难使用的话,你就能够用它。

 

依赖跟踪如何工做的

新手不必知道太清楚,可是高级开发人员能够须要知道为何依赖监控属性可以自动跟踪而且自动更新UI…

事实上,很是简单,甚至说可爱。跟踪的逻辑是这样的:

当你声明一个依赖监控属性的时候,KO会当即调用执行函数而且获取初始化值。

当你的执行函数运行的时候,KO会把全部须要依赖的依赖属性(或者监控依赖属性)都记录到一个Log列表里。

执行函数结束之后,KO会向全部Log里须要依赖到的对象进行订阅。订阅的callback函数是从新运行你的执行函数。而后回头从新执行上面的第一步操做(而且注销再也不使用的订阅)。

最后KO会通知上游全部订阅它的订阅者,告诉它们我已经设置了新值。

全部说,KO不只仅是在第一次执行函数执行时候探测你的依赖项,每次它都会探测。举例来讲,你的依赖属性能够是动态的:依赖属性A表明你是否依赖于依赖属性B或者C,这时候只有当A或者你当前的选择B或者C改变的时候执行函数才从新执行。你不须要再声明其它的依赖:运行时会自动探测到的。

另一个技巧是:一个模板输出的绑定是依赖监控属性的简单实现,若是模板读取一个监控属性的值,那模板绑定就会自动变成依赖监控属性依赖于那个监控属性,监控属性一旦改变,模板绑定的依赖监控属性就会自动执行。嵌套的模板也是自动的:若是模板X render模板 Y,而且Y须要显示监控属性Z的值,当Z改变的时候,因为只有Y依赖它,因此只有Y这部分进行了从新绘制(render)。

三、使用observable数组

若是你要探测和响应一个对象的变化,你应该用observables。若是你须要探测和响应一个集合对象的变化,你应该用observableArray 。在不少场景下,它都很是有用,好比你要在UI上须要显示/编辑的一个列表数据集合,而后对集合进行添加和删除。

var myObservableArray = ko.observableArray();    // Initially an empty array
myObservableArray.push('Some value');            // Adds the value and notifies observers

 

关键点:监控数组跟踪的是数组里的对象,而不是这些对象自身的状态。

简单说,将一对象放在observableArray 里不会使这个对象自己的属性变化可监控的。固然你本身也能够声明这个对象的属性为observable的,但它就成了一个依赖监控对象了。一个observableArray 仅仅监控他拥有的对象,并在这些对象添加或者删除的时候发出通知。

 

预加载一个监控数组observableArray

若是你想让你的监控数组在开始的时候就有一些初始值,那么在声明的时候,你能够在构造器里加入这些初始对象。例如:

// This observable array initially contains three objects
var anotherObservableArray = ko.observableArray([
    { name: "Bungle", type: "Bear" },
    { name: "George", type: "Hippo" },
    { name: "Zippy", type: "Unknown" }
]);

 

从observableArray里读取信息

一个observableArray其实就是一个observable的监控对象,只不过他的值是一个数组(observableArray还加了不少其余特性,稍后介绍)。因此你能够像获取普通的observable的值同样,只须要调用无参函数就能够获取自身的值了。 例如,你能够像下面这样获取它的值:

alert('The length of the array is ' + myObservableArray().length);
alert('The first element is ' + myObservableArray()[0]);

理论上你可使用任何原生的JavaScript数组函数来操做这些数组,可是KO提供了更好的功能等价函数,他们很是有用是由于:

兼容全部浏览器。(例如indexOf不能在IE8和早期版本上使用,但KO本身的indexOf 能够在全部浏览器上使用)

在数组操做函数方面(例如push和splice),KO本身的方式能够自动触发依赖跟踪,而且通知全部的订阅者它的变化,而后让UI界面也相应的自动更新。

语法更方便,调用KO的push方法,只须要这样写:myObservableArray.push(...)。 好比原生数组的myObservableArray().push(...)好用多了。

 

下面讲解的均是observableArray的读取和写入的相关函数。

indexOf

indexOf 函数返回的是第一个等于你参数数组项的索引。例如:myObservableArray.indexOf('Blah')将返回以0为第一个索引的第一个等于Blah的数组项的索引。若是没有找到相等的,将返回-1。

slice

slice函数是observableArray相对于JavaScript 原生函数slice的等价函数(返回给定的从开始索引到结束索引之间全部的对象集合)。 调用myObservableArray.slice(...)等价于调用JavaScript原生函数(例如:myObservableArray().slice(...))。

 

操做observableArray

observableArray 展示的是数组对象类似的函数并通知订阅者的功能。

pop, push, shift, unshift, reverse, sort, splice

全部这些函数都是和JavaScript数组原生函数等价的,惟一不一样的数组改变能够通知订阅者:

    myObservableArray.push('Some new value') 在数组末尾添加一个新项

    myObservableArray.pop() 删除数组最后一个项并返回该项

    myObservableArray.unshift('Some new value') 在数组头部添加一个项

    myObservableArray.shift() 删除数组头部第一项并返回该项

    myObservableArray.reverse() 翻转整个数组的顺序

    myObservableArray.sort() 给数组排序

        默认状况下,是按照字符排序(若是是字符)或者数字排序(若是是数字)。

        你能够排序传入一个排序函数进行排序,该排序函数须要接受2个参数(表明该数组里须要比较的项),若是第一个项小于第二个项,返回-1,大于则返回1,等于返回0。例如:用lastname给person排序,你能够这样写:myObservableArray.sort (function (left, right) {return left.lastName == right.lastName? 0: (left.lastName < right.lastName? -1: 1) })

     myObservableArray.splice() 删除指定开始索引和指定数目的数组对象元素。例如myObservableArray.splice(1, 3) 从索引1开始删除3个元素(第2,3,4个元素)而后将这些元素做为一个数组对象返回。

更多observableArray 函数的信息,请参考等价的JavaScript数组标准函数

 

remove和removeAll

observableArray 添加了一些JavaScript数组默认没有但很是有用的函数:

    myObservableArray.remove(someItem) 删除全部等于someItem的元素并将被删除元素做为一个数组返回

    myObservableArray.remove(function(item) { return item.age < 18 }) 删除全部age属性小于18的元素并将被删除元素做为一个数组返回

    myObservableArray.removeAll(['Chad', 132, undefined]) 删除全部等于'Chad', 123, or undefined的元素并将被删除元素做为一个数组返回

 

destroy和destroyAll(注:一般只和和Ruby on Rails开发者有关)

destroy和destroyAll函数是为Ruby on Rails开发者方便使用为开发的:

    myObservableArray.destroy(someItem) 找出全部等于someItem的元素并给他们添加一个属性_destroy,并赋值为true

    myObservableArray.destroy(function(someItem) { return someItem.age < 18 }) 找出全部age属性小于18的元素并给他们添加一个属性_destroy,并赋值为true

    myObservableArray.destroyAll(['Chad', 132, undefined]) 找出全部等于'Chad', 123, 或undefined 的元素并给他们添加一个属性_destroy,并赋值为true

 

那么,_destroy是作什么用的?正如我提到的,这只是为Rails 开发者准备的。在Rails 开发过程当中,若是你传入一个JSON对象,Rails 框架会自动转换成ActiveRecord对象而且保存到数据库。Rails 框架知道哪些对象以及在数据库中存在,哪些须要添加或更新, 标记_destroy为true就是告诉框架删除这条记录。

 

注意的是:在KO render一个foreach模板的时候,会自动隐藏带有_destroy属性而且值为true的元素。因此若是你的“delete”按钮调用destroy(someItem) 方法的话,UI界面上的相对应的元素将自动隐藏,而后等你提交这个JSON对象到Rails上的时候,这个元素项将从数据库删除(同时其它的元素项将正常的插入或者更新)。

Knockout应用开发指南 第三章:绑定语法(1)

2011-11-23 08:23 by 汤姆大叔, 20105 阅读, 7 评论, 收藏, 编辑

第三章全部代码都须要启用KO的ko.applyBindings(viewModel);功能,才能使代码生效,为了节约篇幅,全部例子均省略了此行代码。

1   visible 绑定

目的

visible绑定到DOM元素上,使得该元素的hidden或visible状态取决于绑定的值。

 

例子

<div data-bind="visible: shouldShowMessage">
    You will see this message only when "shouldShowMessage" holds a true value.
</div>

<script type="text/javascript">
    var viewModel = {
        shouldShowMessage: ko.observable(true) // Message initially visible
    };
    viewModel.shouldShowMessage(false); // ... now it's hidden
    viewModel.shouldShowMessage(true); // ... now it's visible again
</script>

 

参数

    主参数

        当参数设置为一个假值时(例如:布尔值false, 数字值0, 或者null, 或者undefined) ,该绑定将设置该元素的style.display值为none,让元素隐藏。它的优先级高于你在CSS里定义的任何display样式。

        当参数设置为一个真值时(例如:布尔值true,或者非空non-null的对象或者数组) ,该绑定会删除该元素的style.display值,让元素可见。而后你在CSS里自定义的display样式将会自动生效。

        若是参数是监控属性observable的,那元素的visible状态将根据参数值的变化而变化,若是不是,那元素的visible状态将只设置一次而且之后不在更新。

     其它参数

        无

 

注:使用函数或者表达式来控制元素的可见性

你也可使用JavaScript函数或者表达式做为参数。这样的话,函数或者表达式的结果将决定是否显示/隐藏这个元素。例如:

<div data-bind="visible: myValues().length > 0">
    You will see this message only when 'myValues' has at least one member.
</div>

<script type="text/javascript">
    var viewModel = {
        myValues: ko.observableArray([]) // Initially empty, so message hidden
    };
    viewModel.myValues.push("some value"); // Now visible
</script>

 

依赖性

除KO核心类库外,无依赖。

 

2   text 绑定

目的

text 绑定到DOM元素上,使得该元素显示的文本值为你绑定的参数。该绑定在显示<span>或者<em>上很是有用,可是你能够用在任何元素上。

 

例子

Today's message is: <span data-bind="text: myMessage"></span>

<script type="text/javascript">
    var viewModel = {
        myMessage: ko.observable() // Initially blank
    };
    viewModel.myMessage("Hello, world!"); // Text appears
</script>

 

参数

    主参数

    KO将参数值会设置在元素的innerText (IE)或textContent(Firefox和其它类似浏览器)属性上。原来的文本将会被覆盖。

    若是参数是监控属性observable的,那元素的text文本将根据参数值的变化而更新,若是不是,那元素的text文本将只设置一次而且之后不在更新。

    若是你传的是否是数字或者字符串(例如一个对象或者数组),那显示的文本将是yourParameter.toString()的等价内容。

    其它参数

        无

 

1:使用函数或者表达式来决定text

若是你想让你的text更可控,那选择是建立一个依赖监控属性(dependent observable),而后在它的执行函数里编码,决定应该显示什么样的text文本。

例如:

The item is <span data-bind="text: priceRating"></span> today.


<script type="text/javascript">
    var viewModel = {
        price: ko.observable(24.95)
    };

    viewModel.priceRating = ko.dependentObservable(function () {
        returnthis.price() >50?"expensive" : "affordable";
    }, viewModel);
</script>

如今,text文本将在“expensive”和“affordable”之间替换,取决于价格怎么改变。

然而,若是有相似需求的话其实没有必要再声明一个依赖监控属性(dependent observable), 你只须要按照以下代码写JavaScript表达式就能够了:

The item is <span data-bind="text: price() > 50 ? 'expensive' : 'affordable'"></span> today.

结果是同样的,但咱们不须要再声明依赖监控属性(dependent observable)。

 

2:关于HTML encoding

由于该绑定是设置元素的innerText或textContent (而不是innerHTML),因此它是安全的,没有HTML或者脚本注入的风险。例如:若是你编写以下代码:

viewModel.myMessage("<i>Hello, world!</i>");

… 它不会显示斜体字,而是原样输出标签。若是你须要显示HTML内容,请参考html绑定.

 

3:关于IE 6的白空格whitespace

IE6有个奇怪的问题,若是 span里有空格的话,它将自动变成一个空的span。若是你想编写以下的代码的话,那Knockout将不起任何做用:

Welcome, <span data-bind="text: userName"></span> to our web site.

… IE6 将不会显示span中间的那个空格,你能够经过下面这样的代码避免这个问题:

Welcome, <span data-bind="text: userName">&nbsp;</span> to our web site.

IE6之后版本和其它浏览器都没有这个问题

 

依赖性

除KO核心类库外,无依赖。

 

3   html 绑定

目的

html绑定到DOM元素上,使得该元素显示的HTML值为你绑定的参数。若是在你的view model里声明HTML标记而且render的话,那很是有用。

 

例子

<div data-bind="html: details"></div>

<script type="text/javascript">
    var viewModel = {
        details: ko.observable() // Initially blank
    };

    viewModel.details("<em>For further details, view the report <a href='report.html'>here</a>.</em>");
    // HTML content appears
</script> 

 

参数

    主参数

    KO设置该参数值到元素的innerHTML属性上,元素以前的内容将被覆盖。

    若是参数是监控属性observable的,那元素的内容将根据参数值的变化而更新,若是不是,那元素的内容将只设置一次而且之后不在更新。

    若是你传的是否是数字或者字符串(例如一个对象或者数组),那显示的文本将是yourParameter.toString()的等价内容。

    其它参数

        无

 

注:关于HTML encoding

由于该绑定设置元素的innerHTML,你应该注意不要使用不安全的HTML代码,由于有可能引发脚本注入攻击。若是你不确信是否安全(好比显示用户输入的内容),那你应该使用text绑定,由于这个绑定只是设置元素的text 值innerText和textContent。

 

依赖性

除KO核心类库外,无依赖。

 

4   css 绑定

目的

css绑定是添加或删除一个或多个CSS class到DOM元素上。 很是有用,好比当数字变成负数时高亮显示。(注:若是你不想应用CSS class而是想引用style属性的话,请参考style绑定。)

 

例子

<div data-bind="css: { profitWarning: currentProfit() < 0 }">
   Profit Information
</div>
 

<script type="text/javascript">
    var viewModel = {
        currentProfit: ko.observable(150000)
        // Positive value, so initially we don't apply the "profitWarning" class
    };

    viewModel.currentProfit(-50);
    // Causes the "profitWarning" class to be applied
</script>

效果就是当currentProfit 小于0的时候,添加profitWarning CSS class到元素上,若是大于0则删除这个CSS class。

 

参数

    主参数

    该参数是一个JavaScript对象,属性是你的CSS class名称,值是比较用的true或false,用来决定是否应该使用这个CSS class。

    你能够一次设置多个CSS class。例如,若是你的view model有一个叫isServre的属性,

<div data-bind="css: { profitWarning: currentProfit() < 0, majorHighlight: isSevere }">

    非布尔值会被解析成布尔值。例如, 0和null被解析成false,21和非null对象被解析成true。

    若是参数是监控属性observable的,那随着值的变化将会自动添加或者删除该元素上的CSS class。若是不是,那CSS class将会只添加或者删除一次而且之后不在更新。

    你可使用任何JavaScript表达式或函数做为参数。KO将用它的执行结果来决定是否应用或删除CSS class。

    其它参数

        无

 

注:应用的CSS class的名字不是合法的JavaScript变量命名

若是你想使用my-class class,你不能写成这样:

<div data-bind="css: { my-class: someValue }">...</div>

… 由于my-class不是一个合法的命名。解决方案是:在my-class两边加引号做为一个字符串使用。这是一个合法的JavaScript 对象 文字(从JSON技术规格说明来讲,你任什么时候候都应该这样使用,虽然不是必须的)。例如,

<div data-bind="css: { 'my-class': someValue }">...</div>

 

依赖性

除KO核心类库外,无依赖。

 

5   style 绑定

目的

style绑定是添加或删除一个或多个DOM元素上的style值。好比当数字变成负数时高亮显示,或者根据数字显示对应宽度的Bar。(注:若是你不是应用style值而是应用CSS class的话,请参考CSS绑定。)

 

例子

<div data-bind="style: { color: currentProfit() < 0 ? 'red' : 'black' }">
   Profit Information
</div>


<script type="text/javascript">
    var viewModel = {
        currentProfit: ko.observable(150000) // Positive value, so initially black
    };
    viewModel.currentProfit(-50); // Causes the DIV's contents to go red
</script>

当currentProfit 小于0的时候div的style.color是红色,大于的话是黑色。

 

参数

    主参数

    该参数是一个JavaScript对象,属性是你的style的名称,值是该style须要应用的值。

    你能够一次设置多个style值。例如,若是你的view model有一个叫isServre的属性,

<div data-bind="style: { color: currentProfit() < 0 ? 'red' : 'black', fontWeight: isSevere() ? 'bold' : '' }">...</div>

    若是参数是监控属性observable的,那随着值的变化将会自动添加或者删除该元素上的style值。若是不是,那style值将会只应用一次而且之后不在更新。

    你可使用任何JavaScript表达式或函数做为参数。KO将用它的执行结果来决定是否应用或删除style值。

    其它参数

        无

 

注:应用的style的名字不是合法的JavaScript变量命名

若是你须要应用font-weight或者text-decoration,你不能直接使用,而是要使用style对应的JavaScript名称。

    错误: { font-weight: someValue };            正确: { fontWeight: someValue }

    错误: { text-decoration: someValue };      正确: { textDecoration: someValue }

参考:style名称和对应的JavaScript 名称列表

 

依赖性

除KO核心类库外,无依赖。

 

6   attr 绑定

目的

attr 绑定提供了一种方式能够设置DOM元素的任何属性值。你能够设置img的src属性,链接的href属性。使用绑定,当模型属性改变的时候,它会自动更新。

 

例子

<a data-bind="attr: { href: url, title: details }">
    Report
</a>

<script type="text/javascript">
    var viewModel = {
        url: ko.observable("year-end.html"),
        details: ko.observable("Report including final year-end statistics")
    };
</script>

呈现结果是该链接的href属性被设置为year-end.html, title属性被设置为Report including final year-end statistics。

 

参数

    主参数

    该参数是一个JavaScript对象,属性是你的attribute名称,值是该attribute须要应用的值。

    若是参数是监控属性observable的,那随着值的变化将会自动添加或者删除该元素上的attribute值。若是不是,那attribute值将会只应用一次而且之后不在更新。

    其它参数

        无

 

注:应用的属性名字不是合法的JavaScript变量命名

若是你要用的属性名称是data-something的话,你不能这样写:

<div data-bind="attr: { data-something: someValue }">...</div>

… 由于data-something 不是一个合法的命名。解决方案是:在data-something两边加引号做为一个字符串使用。这是一个合法的JavaScript 对象 文字(从JSON技术规格说明来讲,你任什么时候候都应该这样使用,虽然不是必须的)。例如,

<div data-bind="attr: { ‘data-something’: someValue }">...</div>

 

依赖性

除KO核心类库外,无依赖。

Knockout应用开发指南 第三章:绑定语法(2)

2011-11-24 09:00 by 汤姆大叔, 16408 阅读, 22 评论, 收藏, 编辑

7   click 绑定

目的

click绑定在DOM元素上添加事件句柄以便元素被点击的时候执行定义的JavaScript 函数。大部分是用在button,input和链接a上,可是能够在任意元素上使用。

 

例子

<div>
    You've clicked <span data-bind="text: numberOfClicks"></span> times
    <button data-bind="click: incrementClickCounter">Click me</button>
</div>


<script type="text/javascript">
    var viewModel = {
        numberOfClicks: ko.observable(0),
        incrementClickCounter: function () {
            var previousCount =this.numberOfClicks();
            this.numberOfClicks(previousCount +1);
        }
    };
</script>

每次点击按钮的时候,都会调用incrementClickCounter()函数,而后更新自动更新点击次数。

 

参数

    主参数

    Click点击事件时所执行的函数。

    你能够声明任何JavaScript函数 – 不必定非要是view model里的函数。你能够声明任意对象上的任何函数,例如: someObject.someFunction。

    View model上的函数在用的时候有一点点特殊,就是不须要引用对象的,直接引用函数自己就好了,好比直接写incrementClickCounter 就能够了,而无需写成: viewModel.incrementClickCounter(尽管是合法的)。

    其它参数

        无

 

注1:传参数给你的click 句柄

最简单的办法是传一个function包装的匿名函数:

<button data-bind="click: function() { viewModel.myFunction('param1', 'param2') }">
    Click me
</button>

这样,KO就会调用这个匿名函数,里面会执行viewModel.myFunction(),而且传进了'param1' 和'param2'参数。

 

注2:访问事件源对象

有些状况,你可能须要使用事件源对象,Knockout会将这个对象传递到你函数的第一个参数:

<button data-bind="click: myFunction">
    Click me
</button>


 <script type="text/javascript">
     var viewModel = {
         myFunction: function (event) {
             if (event.shiftKey) {
                 //do something different when user has shift key down
             } else {
                 //do normal action
             }
         }
     };
</script>

若是你须要的话,可使用匿名函数的第一个参数传进去,而后在里面调用:

<button data-bind="click: function(event) { viewModel.myFunction(event, 'param1', 'param2') }">
    Click me
</button>

这样,KO就会将事件源对象传递给你的函数而且使用了。

 

注3: 容许执行默认事件

默认状况下,Knockout会阻止冒泡,防止默认的事件继续执行。例如,若是你点击一个a链接,在执行完自定义事件时它不会链接到href地址。这特别有用是由于你的自定义事件主要就是操做你的view model,而不是链接到另一个页面。

固然,若是你想让默认的事件继续执行,你能够在你click的自定义函数里返回true。

 

注4:控制this句柄

初学者能够忽略这小节,由于大部分都用不着,高级用户能够参考以下内容:

KO在调用你定义的函数时,会将view model传给this对象(也就是ko.applyBindings使用的view model)。主要是方便你在调用你在view model里定义的方法的时候能够很容易再调用view model里定义的其它属性。例如: this.someOtherViewModelProperty。

若是你想引用其它对象,咱们有两种方式:

你能够和注1里那样使用匿名函数,由于它支持任意JavaScript 对象。

 

你也能够直接引用任何函数对象。你可使用bind使callback函数设置this为任何你选择的对象。例如:

<button data-bind="click: someObject.someFunction.bind(someObject)">
    Click me
</button>

 

若是你是C#或Java开发人员,你能够疑惑为何咱们还要用bind函数到一个对象想,特别是像调用someObject.someFunction。 缘由是在JavaScript里,函数本身不是类的一部分,他们在单独存在的对象,有可能多个对象都引用一样的someFunction函数,因此当这个函数被调用的时候它不知道谁调用的(设置this给谁)。在你bind以前运行时是不会知道的。KO默认状况下设置this对象是view model,但你能够用bind语法重定义它。

 

在注1里使用匿名函数的时候没有具体的要求,由于JavaScript代码 someObject.someFunction()就意味着调用someFunction,而后设置this到 someObject对象上。

 

注5:防止事件冒泡

默认状况下,Knockout容许click事件继续在更高一层的事件句柄上冒泡执行。例如,若是你的元素和父元素都绑定了click事件,那当你点击该元素的时候两个事件都会触发的。若是须要,你能够经过额外的绑定clickBubble来禁止冒泡。例如:

<div data-bind="click: myDivHandler">
    <button data-bind="click: myButtonHandler, clickBubble: false">
        Click me
    </button>
</div>

默认状况下,myButtonHandler会先执行,而后会冒泡执行myDivHandler。但一旦你设置了clickBubble为false的时候,冒泡事件会被禁止。

 

依赖性

除KO核心类库外,无依赖。

 

8   event 绑定

目的

event绑定在DOM元素上添加指定的事件句柄以便元素被触发的时候执行定义的JavaScript 函数。大部分状况下是用在keypress,mouseover和mouseout上。

 

例子

<div>
    <div data-bind="event: { mouseover: enableDetails, mouseout: disableDetails }">
        Mouse over me
    </div>
    <div data-bind="visible: detailsEnabled">
        Details
    </div>
</div>


<script type="text/javascript">
    var viewModel = {
        detailsEnabled: ko.observable(false),
        enableDetails: function () {
            this.detailsEnabled(true);
        },
        disableDetails: function () {
            this.detailsEnabled(false);
        }
    };
</script>

每次鼠标在第一个元素上移入移出的时候都会调用view model上的方法来toggle detailsEnabled的值,而第二个元素会根据detailsEnabled的值自动显示或者隐藏。

 

参数

    主参数

    你须要传入的是一个JavaScript对象,他的属性名是事件名称,值是你所须要执行的函数。

    你能够声明任何JavaScript函数 – 不必定非要是view model里的函数。你能够声明任意对象上的任何函数,例如: event: { mouseover: someObject.someFunction }。

    View model上的函数在用的时候有一点点特殊,就是不须要引用对象的,直接引用函数自己就好了,好比直接写event: { mouseover: enableDetails } 就能够了,而无需写成: event: { mouseover: viewModel.enableDetails }(尽管是合法的)。

    其它参数

        无

 

注1:传参数给你的click 句柄

最简单的办法是传一个function包装的匿名函数:

<button data-bind="event: { mouseover: function() { viewModel.myFunction('param1', 'param2') } }">
    Click me
</button>

这样,KO就会调用这个匿名函数,里面会执行viewModel.myFunction(),而且传进了'param1' 和'param2'参数。

 

注2:访问事件源对象

有些状况,你可能须要使用事件源对象,Knockout会将这个对象传递到你函数的第一个参数:

<div data-bind="event: { mouseover: myFunction }">
    Mouse over me
</div>

 
 <script type="text/javascript">
     var viewModel = {
         myFunction: function (event) {
             if (event.shiftKey) {
                 //do something different when user has shift key down
             } else {
                 //do normal action
             }
         }
     };
</script>

若是你须要的话,可使用匿名函数的第一个参数传进去,而后在里面调用:

<div data-bind="event: { mouseover: function(event) { viewModel.myFunction(event, 'param1', 'param2') } }">
    Mouse over me
</div>

这样,KO就会将事件源对象传递给你的函数而且使用了。

 

注3: 容许执行默认事件

默认状况下,Knockout会阻止冒泡,防止默认的事件继续执行。例如,若是在一个input标签上绑定一个keypress事件,当你输入内容的时候,浏览器只会调用你的函数而不是天价你输入的值。另一个例子click绑定,当你点击一个a链接,在执行完自定义事件时它不会链接到href地址。由于你的自定义事件主要就是操做你的view model,而不是链接到另一个页面。

固然,若是你想让默认的事件继续执行,你能够在你event的自定义函数里返回true。

 

注4:控制this句柄

初学者能够忽略这小节,由于大部分都用不着,高级用户能够参考以下内容:

KO在调用你定义的event绑定函数时,会将view model传给this对象(也就是ko.applyBindings使用的view model)。主要是方便你在调用你在view model里定义的方法的时候能够很容易再调用view model里定义的其它属性。例如: this.someOtherViewModelProperty。

 

若是你想引用其它对象,咱们有两种方式:

你能够和注1里那样使用匿名函数,由于它支持任意JavaScript 对象。

你也能够直接引用任何函数对象。你可使用bind使callback函数设置this为任何你选择的对象。例如:

<div data-bind="event: { mouseover: someObject.someFunction.bind(someObject) }">
    Mouse over me
</div>

若是你是C#或Java开发人员,你能够疑惑为何咱们还要用bind函数到一个对象想,特别是像调用someObject.someFunction。 缘由是在JavaScript里,函数本身不是类的一部分,他们在单独存在的对象,有可能多个对象都引用一样的someFunction函数,因此当这个函数被调用的时候它不知道谁调用的(设置this给谁)。在你bind以前运行时是不会知道的。KO默认状况下设置this对象是view model,但你能够用bind语法重定义它。

 

在注1里使用匿名函数的时候没有具体的要求,由于JavaScript代码 someObject.someFunction()就意味着调用someFunction,而后设置this到 someObject对象上。

 

注5:防止事件冒泡

默认状况下,Knockout容许event事件继续在更高一层的事件句柄上冒泡执行。例如,若是你的元素和父元素都绑定了mouseover事件,那么若是你的鼠标在该元素移动的时候两个事件都会触发的。若是须要,你能够经过额外的绑定youreventBubble来禁止冒泡。例如:

<div data-bind="event: { mouseover: myDivHandler }">
    <button data-bind="event: { mouseover: myButtonHandler }, mouseoverBubble: false">
        Click me
    </button>
</div>

默认状况下,myButtonHandler会先执行,而后会冒泡执行myDivHandler。但一旦你设置了mouseoverBubble为false的时候,冒泡事件会被禁止。

 

依赖性

除KO核心类库外,无依赖。

 

9   submit 绑定

目的

submit绑定在form表单上添加指定的事件句柄以便该form被提交的时候执行定义的JavaScript 函数。只能用在表单form元素上。

当你使用submit绑定的时候, Knockout会阻止form表单默认的submit动做。换句话说,浏览器会执行你定义的绑定函数而不会提交这个form表单到服务器上。能够很好地解释这个,使用submit绑定就是为了处理view model的自定义函数的,而不是再使用普通的HTML form表单。若是你要继续执行默认的HTML form表单操做,你能够在你的submit句柄里返回true。

 

例子

<form data-bind="submit: doSomething">
    ... form contents go here ...
    <button type="submit">Submit</button>
</div>

<script type="text/javascript">
    var viewModel = {
        doSomething: function (formElement) {
            // ... now do something
        }
    };
</script>

这个例子里,KO将把整个form表单元素做为参数传递到你的submit绑定函数里。 你能够忽略无论,可是有些例子里是否有用,参考:ko.postJson工具。

 

为何不在submit按钮上使用click绑定?

在form上,你可使用click绑定代替submit绑定。不过submit能够handle其它的submit行为,好比在输入框里输入回车的时候能够提交表单。

 

参数

    主参数

    你绑定到submit事件上的函数

    你能够声明任何JavaScript函数 – 不必定非要是view model里的函数。你能够声明任意对象上的任何函数,例如: submit: someObject.someFunction。

    View model上的函数在用的时候有一点点特殊,就是不须要引用对象的,直接引用函数自己就好了,好比直接写submit: doSomething就能够了,而无需写成: submit: viewModel. doSomething(尽管是合法的)。

    其它参数

        无

 

备注:

关于若是传递更多的参数给submit绑定函数,或者当调用非view model里的函数的时如何控制this,请参考click绑定。全部click绑定相关的notes也都适用于submit绑定。

 

依赖性

除KO核心类库外,无依赖。

 

10   enable 绑定

目的

enable绑定使DOM元素只有在参数值为 true的时候才enabled。在form表单元素input,select,和textarea上很是有用。

 

例子

<p>
    <input type='checkbox' data-bind="checked: hasCellphone"/>
    I have a cellphone
</p>

<p>
    Your cellphone number:
    <input type='text' data-bind="value: cellphoneNumber, enable: hasCellphone"/>
</p>
 
<script type="text/javascript">
    var viewModel = {
        hasCellphone: ko.observable(false),
        cellphoneNumber: ""
    };
</script>

这个例子里,“Your cellphone number”后的text box 初始状况下是禁用的,只有当用户点击标签 “I have a cellphone”的时候才可用。

 

参数

    主参数

    声明DOM元素是否可用enabled。

    非布尔值会被解析成布尔值。例如0和null被解析成false,21和非null对象被解析给true。

    若是你的参数是observable的,那绑定会随着observable值的改变而自动更新enabled/disabled状态。若是不是,则只会设置一次而且之后再也不更新。

    其它参数

         无

 

注:任意使用JavaScript表达式

不牢牢限制于变量 – 你可使用任何JavaScript表达式来控制元素是否可用。例如,

<button data-bind="enabled: parseAreaCode(viewModel.cellphoneNumber()) != '555'">
    Do something
</button>

 

依赖性

除KO核心类库外,无依赖。

 

11   disable 绑定

目的

disable绑定使DOM元素只有在参数值为 true的时候才disabled。在form表单元素input,select,和textarea上很是有用。

disable绑定和enable绑定正好相反,详情请参考enable绑定。

Knockout应用开发指南 第三章:绑定语法(3)

2011-11-24 12:52 by 汤姆大叔, 18882 阅读, 9 评论, 收藏, 编辑

12   value 绑定

目的

value绑定是关联DOM元素的值到view model的属性上。主要是用在表单控件<input>,<select>和<textarea>上。

当用户编辑表单控件的时候, view model对应的属性值会自动更新。一样,当你更新view model属性的时候,相对应的元素值在页面上也会自动更新。

注:若是你在checkbox或者radio button上使用checked绑定来读取或者写入元素的 checked状态,而不是value 值的绑定。

 

例子

<p>Login name: <input data-bind="value: userName"/></p>
<p>Password: <input type="password" data-bind="value: userPassword"/></p>
 

<script type="text/javascript">
    var viewModel = {
        userName: ko.observable(""),        // Initially blank
        userPassword: ko.observable("abc"), // Prepopulate
    };
</script>

 

参数

    主参数

    KO设置此参数为元素的value值。以前的值将被覆盖。

    若是参数是监控属性observable的,那元素的value值将根据参数值的变化而更新,若是不是,那元素的value值将只设置一次而且之后不在更新。

    若是你提供的参数不是一个数字或者字符串(而是对象或者数组)的话,那显示的value值就是yourParameter.toString() 的内容(一般没用,因此最好都设置为数字或者字符串)。

    无论何时,只要你更新了元素的值,那 KO都会将view model对应的属性值自动更新。默认状况下当用户离开焦点(例如onchange事件)的时候,KO才更新这个值,可是你能够经过第2个参数valueUpdate来特别指定改变值的时机。

 

    其它参数

        valueUpdate

        若是你使用valueUpdate参数,那就是意味着KO将使用自定义的事件而不是默认的离开焦点事件。下面是一些最经常使用的选项:

            “change”(默认值) - 当失去焦点的时候更新view model的值,或者是<select> 元素被选择的时候。

            “keyup” – 当用户敲完一个字符之后当即更新view model。

            “keypress” – 当用户正在敲一个字符但没有释放键盘的时候就当即更新view model。不像 keyup,这个更新和keydown是同样的。

            “afterkeydown” – 当用户开始输入字符的时候就更新view model。主要是捕获浏览器的keydown事件或异步handle事件。

        上述这些选项,若是你想让你的view model进行实时更新,使用“afterkeydown”是最好的选择。

 

例子:

<p>Your value: <input data-bind="value: someValue, valueUpdate: 'afterkeydown'"/></p>
<p>You have typed: <span data-bind="text: someValue"></span></p> <!-- updates in real-time -->

<script type="text/javascript">
    var viewModel = {
        someValue: ko.observable("edit me")
    };
</script>

 

注1:绑定下拉菜单drop-down list(例如SELECT)

Knockout对下拉菜单drop-down list绑定有一个特殊的支持,那就是在读取和写入绑定的时候,这个值能够是任意JavaScript对象,而没必要非得是字符串。在你让你用户选择一组model对象的时候很是有用。具体例子,参考options绑定。

相似,若是你想建立一个multi-select list,参考selectedOptions绑定。

 

注2:更新observable和non-observable属性值

若是你用value绑定将你的表单元素和你的observable属性关联起来,KO设置的2-way的双向绑定,任何一方改变都会更新另一方的值。

可是,若是你的元素绑定的是一个non-observable属性(例如是一个原始的字符串或者JavaScript表达式) ,KO会这样执行:

若是你绑定的non-observable属性是简单对象,例如一个常见的属性值,KO会设置这个值为form表单元素的初始值,若是你改变form表单元素的值,KO会将值从新写回到view mode的这个属性。但当这个属性本身改变的时候,元素却不会再变化了(由于不是observable的),因此它仅仅是1-way绑定。

若是你绑定的non-observable属性是复杂对象,例如复杂的JavaScript 表达式或者子属性,KO也会设置这个值为form表单元素的初始值,可是改变form表单元素的值的时候,KO不会再写会view model属性,这种状况叫one-time-only value setter,不是真正的绑定。

 

例子:

<p>First value: <input data-bind="value: firstValue"/></p>          <!-- two-way binding -->
<p>Second value: <input data-bind="value: secondValue"/></p>        <!-- one-way binding -->
<p>Third value: <input data-bind="value: secondValue.length"/></p>  <!-- no binding -->

<script type="text/javascript">
    var viewModel = {
        firstValue: ko.observable("hello"), // Observable
        secondValue: "hello, again"// Not observable
    };
    ko.applyBindings(viewModel);
</script>

 

依赖性

除KO核心类库外,无依赖。

 

13   checked 绑定

目的

checked绑定是关联到checkable的form表单控件到view model上 - 例如checkbox(<input type='checkbox'>)或者radio button(<input type='radio'>) 。当用户check关联的form表单控件的时候,view model对应的值也会自动更新,相反,若是view model的值改变了,那控件元素的check/uncheck状态也会跟着改变。

注:对text box,drop-down list和全部non-checkable的form表单控件,用value绑定来读取和写入是该元素的值,而不是checked绑定。

例子

<p>Send me spam: <input type="checkbox" data-bind="checked: wantsSpam"/></p>

<script type="text/javascript">
    var viewModel = {
        wantsSpam: ko.observable(true) // Initially checked
    };

     // ... then later ...
    viewModel.wantsSpam(false); // The checkbox becomes unchecked
</script>

 

Checkbox关联到数组

<p>Send me spam: <input type="checkbox" data-bind="checked: wantsSpam"/></p>
<div data-bind="visible: wantsSpam">
    Preferred flavors of spam:
    <div><input type="checkbox" value="cherry" data-bind="checked: spamFlavors"/> Cherry</div>
    <div><input type="checkbox" value="almond" data-bind="checked: spamFlavors"/> Almond</div>
    <div><input type="checkbox" value="msg" data-bind="checked: spamFlavors"/> Monosodium Glutamate</div>
</div>

 
<script type="text/javascript">

    var viewModel = {
        wantsSpam: ko.observable(true),
        spamFlavors: ko.observableArray(["cherry", "almond"]) // Initially checks the Cherry and Almond checkboxes
    };

    // ... then later ...
    viewModel.spamFlavors.push("msg"); // Now additionally checks the Monosodium Glutamate checkbox
</script>

 

添加radio button

<p>Send me spam: <input type="checkbox" data-bind="checked: wantsSpam"/></p>

<div data-bind="visible: wantsSpam">
    Preferred flavor of spam:
    <div><input type="radio" name="flavorGroup" value="cherry" data-bind="checked: spamFlavor"/> Cherry</div>
    <div><input type="radio" name="flavorGroup" value="almond" data-bind="checked: spamFlavor"/> Almond</div>
    <div><input type="radio" name="flavorGroup" value="msg" data-bind="checked: spamFlavor"/> Monosodium Glutamate</div>
</div>

 
<script type="text/javascript">

    var viewModel = {
        wantsSpam: ko.observable(true),
        spamFlavor: ko.observable("almond") // Initially selects only the Almond radio button
    };

     // ... then later ...
    viewModel.spamFlavor("msg"); // Now only Monosodium Glutamate is checked
</script>

 

参数

    主参数

    KO会设置元素的checked状态匹配到你的参数上,以前的值将被覆盖。对参数的解析取决于你元素的类型:

        对于checkbox,当参数为true的时候,KO会设置元素的状态为checked,反正设置为unchecked。若是你传的参数不是布尔值,那KO将会解析成布尔值。也就是说非0值和非null对象,非空字符串将被解析成true,其它值都被解析成false。

        当用户check或者uncheck这个checkbox的时候,KO会将view model的属性值相应地设置为true或者false。

        一个特殊状况是参数是一个数组,若是元素的值存在于数组,KO就会将元素设置为checked,若是数组里不存在,就设置为unchecked。若是用户对checkbox进行check或uncheck,KO就会将元素的值添加数组或者从数组里删除。

        对于radio buttons,KO只有当参数值等于radio button value属性值的时候才设置元素为checked状态。因此参数应是字符串。在上面的例子里只有当view model 的spamFlavor 属性等于“almond”的时候,该radio button才会设置为checked。

        当用户将一个radio button选择上的时候 is selected,KO会将该元素的value属性值更新到view model属性里。上面的例子,当点击value= “cherry”的选项上, viewModel.spamFlavor的值将被设置为“cherry”。

        固然,最有用的是设置一组radio button元素对应到一个单个的view model 属性。确保一次只能选择一个radio button须要将他们的name属性名都设置成同样的值(例如上个例子的flavorGroup值)。这样的话,一次就只能选择一个了。

    若是参数是监控属性observable的,那元素的checked状态将根据参数值的变化而更新,若是不是,那元素的value值将只设置一次而且之后不在更新。

    其它参数

    无

 

依赖性

除KO核心类库外,无依赖。

 

14   options 绑定

目的

options绑定控制什么样的options在drop-down列表里(例如:<select>)或者 multi-select 列表里 (例如:<select size='6'>)显示。此绑定不能用于<select>以外的元素。关联的数据应是数组(或者是observable数组),<select>会遍历显示数组里的全部的项。

 

注:对于multi-select列表,设置或者获取选择的多项须要使用selectedOptions绑定。对于single-select列表,你也可使用value绑定读取或者设置元素的selected项。

 

例1:Drop-down list

<p>Destination country: <select data-bind="options: availableCountries"></select></p>

 
<script type="text/javascript">
    var viewModel = {
        availableCountries: ko.observableArray(['France', 'Germany', 'Spain']) // These are the initial options
    };

    // ... then later ...
    viewModel.availableCountries.push('China'); // Adds another option
</script>

 

例2:Multi-select list

<p>Choose some countries you'd like to visit: <select data-bind="options: availableCountries" size="5" multiple="true"></select></p>
 
<script type="text/javascript">
    var viewModel = {
        availableCountries: ko.observableArray(['France', 'Germany', 'Spain'])
    };
</script>

 

例3:Drop-down list展现的任意JavaScript对象,不只仅是字符串

<p>
    Your country:
    <select data-bind="options: availableCountries,
              optionsText: 'countryName', value: selectedCountry, optionsCaption: 'Choose...'"></select>
</p>

<div data-bind="visible: selectedCountry"> <!-- Appears when you select something -->
    You have chosen a country with population
    <span data-bind="text: selectedCountry() ? selectedCountry().countryPopulation : 'unknown'"></span>.
</div>

<script type="text/javascript">
    // Constructor for an object with two properties
var country =function (name, population) {
        this.countryName = name;
        this.countryPopulation = population;
    };

     var viewModel = {
        availableCountries: ko.observableArray([
            new country("UK", 65000000),
            new country("USA", 320000000),
            new country("Sweden", 29000000)
        ]),
        selectedCountry: ko.observable() // Nothing selected by default
    };
</script>

 

例4:Drop-down list展现的任意JavaScript对象,显示text是function的返回值

<!-- Same as example 3, except the <select> box expressed as follows: -->

<select data-bind="options: availableCountries,
                   optionsText: function(item) {
                       return item.countryName + ' (pop: ' + item.countryPopulation + ')'
                   },
                   value: selectedCountry,
                   optionsCaption: 'Choose...'"></select>

 

注意例3和例4在optionsText值定义上的不一样。

 

参数

    主参数

    该参数是一个数组(或者observable数组)。对每一个item,KO都会将它做为一个<option> 添加到<select>里,以前的options都将被删除。

    若是参数是一个string数组,那你不须要再声明任何其它参数。<select>元素会将每一个string显示为一个option。不过,若是你让用户选择的是一个JavaScript对象数组(不只仅是string),那就须要设置optionsText和optionsValue这两个参数了。

    若是参数是监控属性observable的,那元素的options项将根据参数值的变化而更新,若是不是,那元素的value值将只设置一次而且之后不在更新。

 

    其它参数

        optionsCaption

        有时候,默认状况下不想选择任何option项。可是single-select drop-down列表因为每次都要默认选择以项目,怎么避免这个问题呢?经常使用的方案是加一个“请选择的”或者“Select an item”的提示语,或者其它相似的,而后让这个项做为默认选项。

        咱们使用optionsCaption参数就能很容易实现,它的值是字符串型,做为默认项显示。例如:

        <select data-bind='options: myOptions, optionsCaption: "Select an item...", value: myChosenValue'></select>

        KO会在全部选项上加上这一个项,而且设置value值为undefined。因此,若是myChosenValue被设置为undefined(默认是observable的),那么上述的第一个项就会被选中。

 

        optionsText

        上面的例3展现的绑定JavaScript对象到option上 – 不只仅是字符串。这时候你须要设置这个对象的那个属性做为drop-down列表或multi-select列表的text来显示。例如,例3中使用的是设置额外的参数optionsText将对象的属性名countryName做为显示的文本。

        若是不想仅仅显示对象的属性值做为每一个item项的text值,那你能够设置optionsText 为JavaScript 函数,而后再函数里经过本身的逻辑返回相应的值(该函数参数为item项自己)。例4展现的就是返回item的2个属性值合并的结果。

 

        optionsValue

        和optionsText相似, 你也能够经过额外参数optionsValue来声明对象的那个属性值做为该<option>的value值。

        经典场景:如在更新options的时候想保留原来的已经选择的项。例如,当你重复屡次调用Ajax获取car列表的时候,你要确保已经选择的某个car一直都是被选择上,那你就须要设置optionsValue为“carId”或者其它的unique标示符,不然的话KO找不知道以前选择的car是新options里的哪一项。

 

        selectedOptions

        对于multi-select列表,你能够用selectedOptions读取和设置多个选择项。技术上看它是一个单独的绑定,有本身的文档,请参考: selectedOptions绑定。

 

注:已经被选择的项会再options改变的时候保留

当使用options绑定<select>元素的时候,若是options改变,KO将尽量第保留以前已经被选择的项不变(除非是你事先手工删除一个或多个已经选择的项)。这是由于options 绑定尝试依赖value值的绑定(single-select列表)和selectedOptions绑定(multi-select列表)。

 

依赖性

除KO核心类库外,无依赖。

 

15   selectedOptions 绑定

目的

selectedOptions绑定用于控制multi-select列表已经被选择的元素,用在使用options绑定的<select>元素上。

当用户在multi-select列表选择或反选一个项的时候,会将view model的数组进行相应的添加或者删除。一样,若是view model上的这个数组是observable数组的话,你添加或者删除任何item(经过push或者splice)的时候,相应的UI界面里的option项也会被选择上或者反选。这种方式是2-way绑定。

注:控制single-select下拉菜单选择项,你可使用value绑定。

 

例子

<p>
    Choose some countries you'd like to visit:
    <select data-bind="options: availableCountries, selectedOptions: chosenCountries" size="5" multiple="true"></select>
</p>

<script type="text/javascript">
    var viewModel = {
        availableCountries: ko.observableArray(['France', 'Germany', 'Spain']),
        chosenCountries: ko.observableArray(['Germany']) // Initially, only Germany is selected
    };

    // ... then later ...
    viewModel.chosenCountries.push('France'); // Now France is selected too
</script>

 

参数

    主参数

    该参数是数组(或observable数组)。KO设置元素的已选项为和数组里match的项,以前的已选择项将被覆盖。

    若是参数是依赖监控属性observable数组,那元素的已选择项selected options项将根据参数值的变化(经过push,pop,或其它observable数组方法)而更新,若是不是,那元素的已选择项selected options将只设置一次而且之后不在更新。

    无论该参数是否是observable数组,用户在multi-select列表里选择或者反选的时候,KO都会探测到,而且更新数组里的对象以达到同步的结果。这样你就能够获取options已选项。

    其它参数

           无

 

注:支持让用户选择任意JavaScript对象

在上面的例子里,用户能够选择数组里的字符串值,可是选择不限于字符串,若是你愿意你能够声明包含任意JavaScript对象的数组,查看options绑定如何显示JavaScript对象到列表里。

这种场景,你能够用selectedOptions来读取或设置这些对象自己,而不是页面上显示的option表示形式,这样作在大部分状况下都很是清晰。view model就能够探测到你从数组对象里选择的项了,而没必要关注每一个项和页面上展现的option项是如何map的。

 

依赖性

除KO核心类库外,无依赖。

 

16   uniqueName 绑定

目的

uniqueName绑定确保所绑定的元素有一个非空的name属性。若是该元素没有name属性,那绑定会给它设置一个unique的字符串值做为name属性。你不会常常用到它,只有在某些特殊的场景下才用到,例如:

在使用KO的时候,一些技术可能依赖于某些元素的name属性,尽快他们没有什么意义。例如,jQuery Validation验证当前只验证有name属性的元素。为配合Knockout UI使用,有些时候须要使用uniqueName绑定避免让jQuery Validation验证出错。

IE 6下,若是radio button没有name属性是不容许被checked了。大部分时候都没问题,由于大部分时候radio button元素都会有name属性的做为一组互相的group。不过,若是你没声明,KO内部会在这些元素上使用uniqueName那么以确保他们能够被checked。

 

例子

<input data-bind="value: someModelProperty, uniqueName: true"/>

 

参数

    主参数

    就像上面的例子同样,传入true(或者能够转成true的值)以启用uniqueName绑定。

    其它参数

        无

 

依赖性

除KO核心类库外,无依赖。

Knockout应用开发指南 第四章:模板绑定

2011-11-25 09:01 by 汤姆大叔, 14468 阅读, 17 评论, 收藏, 编辑

模板绑定The template binding

目的

template绑定经过模板将数据render到页面。模板绑定对于构建嵌套结构的页面很是方便。默认状况, Knockout用的是流行的jquery.tmpl模板引擎。使用它的话,须要在安装页面下载和引用jquery.tmpl和jQuery框架。或者你 也能够集成其它的模板引擎(虽然须要了解Knockout 内部知识才行)。

 

例子

<div data-bind='template: "personTemplate"'> </div>
<script id='personTemplate' type='text/html'>
    ${ name } is ${ age } years old
    <button data-bind='click: makeOlder'>Make older</button>
</script>

<script type='text/javascript'>
    var viewModel = {
        name: ko.observable('Bert'),
        age: ko.observable(78),
        makeOlder: function () {
            this.age(this.age() +1);
        }
    };
    ko.applyBindings(viewModel);
</script>

当引用的observable(dependent observable)数据改变的时候,Knockout会自动从新render模板。在这个例子里,每次点击button的时候都会从新render模板。

 

语法

你可使用任何你模板引擎支持的语法。jquery.tmpl执行以下语法:

${ someValue } — 参考文档

{{html someValue}} — 参考文档

{{if someCondition}} — 参考文档

{{else someCondition}} — 参考文档

{{each someArray}} — 参考文档

 

和observable数组一块儿使用{{each}}

固然使用{{each someArray}}的时候,若是你的值是observableArray,你必须使用JavaScript类型的基础数组类型{{each myObservableArray()}},而不是{{each myObservableArray}}。

 

参数

    主参数

        语法快速记忆:若是你声明的仅仅是字符串(上个例子),KO会使用模板的ID来render。应用在模板上的数据是你的整个view model对象(例如ko.applyBindings 绑定的对象)。

        更多控件,你能够传带有以下属性的JavaScript对象:

            name(必选项) — 须要render的模板ID – 参考 注5 如何使用function函数声明ID。

            data(可选项) — 须要render到模板的数据。若是你忽略整个参数,KO将查找foreach参数,或者是应用整个view model对象。

            foreach(可选项) — 指定KO按照“foreach”模式render模板 – 参考 注3。

            afterAdd或beforeRemove(可选项) — 在foreach模式下使用callback函数。

            templateOptions(可选项) — 在render模板的时候,传递额外数据以便使用。参考 注6。

 

传递多个参数的例子:

<div data-bind='template: { name: "personTemplate", data: someObject }'> </div>

 

注1:Render嵌套模板

由于在模板里使用的是data-bind属性来声明的,因此嵌套模板你能够再次使用data-bind='template: ...',在上层模板的元素里。

这比模板引发的原生语法好用多了(例如jquery.tmpl里的{{tmpl}})。Knockout语法的好处在于能够在每层模板的跟着相关的依赖值,因此若是依赖改变了,KO将只会从新render依赖所在的那个模板。这将很大地改善了性能。

 

注2:${ val }和<span data-bind='text: val'></span>有何不一样?

当你在模板内部使用data-bind属性的时候,KO是单独为这个绑定单独跟踪依赖项的。当model改变的时候,KO只会更新绑定的元素以及子元素而不须要从新render整个模板。因此若是你声明这样的代码是<span data-bind='text: someObservableValue'></span>,当 someObservableValue改变的时候,KO将只是简单地更新<span>元素的text值而不须要从新render整个模板。

不过,若是模板内部使用的observable值(例如${ someObservableValue }),若是这个observable值改变了,那KO将从新render整个模板。

这就是说,不少状况下<span data-bind='text: someObservableValue'></span>性能要比${ someObservableValue }要好,由于值改变的话不会影响临近元素的状态。不过${ someObservableValue }语法比较简洁,若是你的模板比较小的话,仍是更合适的,不会带来大的性能问题。

 

注3:使用foreach

若是须要为集合里的每个item render一次模板,有2种方式:

    你可使用模板引擎里的原生“each”语法,对jquery.tmpl来讲就是用{{each}}语法迭代数组。

    另一种方式就是用Knockout的foreach模式来render。

 

例子:

<div data-bind='template: { name: "personTemplate",
                            foreach: someObservableArrayOfPeople }'> </div>

 

foreach模板模式的好处是:

当往你的collection集合里添加新item项的时候,KO只会对这个新item进行render模板,而且将结果附加到现有的DOM上。

当从collection集合里删除item的时候,KO将不会从新render任何模板,而只是简单地删除相关的元素。

KO容许经过自定义的方式声明afterAdd和beforeRemove的callback函数添加/删除DOM元素。而后这个callback会在删除元素的时候进行一些动画或者其它操做。

与原生的each不一样之处是:在改变以后,模板引擎强制从新render模板里全部的内容,由于它根本就不关注KO里所谓的依赖跟踪内容。

关于使用foreach模式的例子,参考grid editor和animated transitions。

 

注4:使用afterRender选项

有时候,你须要在模板生成的DOM元素上深度定义逻辑。例如,你可能想再模板输出的时候进行截获,而后在render的元素上容许jQuery UI命令(好比date picker,slider,或其它)。

你可使用afterRender选项,简单声明一个function函数(匿名函数或者view model里的函数),在render或者从新render模板以后Knockout会从新调用它。若是你使用的是foreach,那在每一个item添加到observable数组以后, Knockout会当即调用afterRender的callback函数。例如,

<div data-bind='template: { name: "personTemplate",
                            data: myData,
                            afterRender: myPostProcessingLogic }'> </div>

… 在view model里声明一个相似的函数(例如,对象包含myData):

viewModel.myPostProcessingLogic = function (elements) {
    // "elements" is an array of DOM nodes just rendered by the template
    // You can add custom post-processing logic here
}

 

注5:动态决定使用哪一个模板

有时候,你可能须要根据数据的状态来决定使用哪一个模板的ID。能够经过function的返回ID应用到name选择上。若是你用的是foreach模板模式, Knockout会对每一个item执行function(将item做为参数)从而将返回值做为ID,不然,该function接受的参数是整个 data option或者是整个view model。

 

例子:

<ul data-bind='template: { name: displayMode,
                           foreach: employees }'> </ul>
<script type='text/javascript'>
var viewModel = {
    employees: ko.observableArray([
        { name: "Kari", active: ko.observable(true) },
        { name: "Brynn", active: ko.observable(false) },
        { name: "Nora", active: ko.observable(false) }
    ]),
    displayMode: function (employee) {
        return employee.active() ?"active" : "inactive";
        // Initially "Kari" uses the "active" template, while the others use "inactive"
    }
};

// ... then later ...
viewModel.employees()[1].active(true);
// Now "Brynn" is also rendered using the "active" template.
</script>

若是你的function引用的是observable值,那当这些值改变的时候,绑定的值会随着改变的。这将致使相应的模板从新render。

 

注6:使用templateOptions传递额外的参数

若是你在绑定模板的时候须要传入额外的数据的话,你可使用templateOptions对象来传递这些值。这能够帮助你经过一些 不属于view model过滤条件或者字符来重用模板。另一个好处是用在范围控制,你能够引用经过你的模板访问怒道的数据。

 

例子,

<ul data-bind='template: { name: "personTemplate",
                           foreach: employees,
                           templateOptions: { label: "Employee:",
                                              selectedPerson: selectedEmployee } }'> </ul>


<script id='personTemplate' type='text/html'>
    <div data-bind="css: { selected: $data === $item.selectedPerson()" }">
        ${ $item.label } <input data-bind="value: name" />
    </div>
</script>

在整个例子里,personTemplate有可能都使用employee和自定义对象。经过templateOptions咱们能够传递一个字符label和当前已选择项做为selectedPerson来控制style。在jquery.tmpl模板里,这些值能够经过访问$item对象的属性获得。

 

注7:模板是被预编译和缓存的

为了最大性能,Knockout内嵌模板引擎jquery.tmpl会利用自身的功能对你的模板进行预编译成可执行的JavaScript代码,而后从编译流程里缓存输出。这将使模板更快更加具备可执行性,尤为是是使用foreach循环来render相同模板的时候。

通常状况你不会注意到这个,因此常常会忘记。不过,当你在某种缘由下经过编程重写模板<script>元素的时候而且该模板以前已经用过一次的话,你的改变不会带来任何render的变化,由于在第一次使用的时候已经预编译了而且缓存起来了。(若是这些会带来问题,咱们将考虑在KO新版本里提供一个禁用或重设模板缓存的功能,不过好像没有好的缘由去动态改变模板<script>元素的内容)。

 

注8:使用不一样的模板引擎

若是你想使用不一样的JavaScript模板引擎(或者是由于某些缘由你不想在jQuery上使用依赖)。咱们能够去为KO来写一个不一样的模板引擎,例如,在KO源代码里的jqueryTmplTemplateEngine.js,尽管他是为了支持多个版本的jquery.tmpl而编译。支持一个单独的模板引擎版本相对简单多了。

 

依赖性

template绑定只能在引用合适的模板引擎状况下才能工做。例如提到的jquery.tmpl引擎。

Knockout应用开发指南 第五章:建立自定义绑定

2011-11-26 19:20 by 汤姆大叔, 9751 阅读, 3 评论, 收藏, 编辑

建立自定义绑定

你能够建立本身的自定义绑定 – 没有必要非要使用内嵌的绑定(像click,value等)。你能够你封装复杂的逻辑或行为,自定义很容易使用和重用的绑定。例如,你能够在form表单里自定义像grid,tabset等这样的绑定。

重要:如下文档只应用在Knockout 1.1.1和更高版本,Knockout 1.1.0和之前的版本在注册API上是不一样的。

 

注册你的绑定

添加子属性到ko.bindingHandlers来注册你的绑定:

ko.bindingHandlers.yourBindingName = {
    init: function(element, valueAccessor, allBindingsAccessor, viewModel) {
        // This will be called when the binding is first applied to an element
        // Set up any initial state, event handlers, etc. here
    },

    update: function(element, valueAccessor, allBindingsAccessor, viewModel) {
        // This will be called once when the binding is first applied to an element,
        // and again whenever the associated observable changes value.
        // Update the DOM element based on the supplied values here.
    }
};

… 而后就能够在任何DOM元素上使用了:

<div data-bind="yourBindingName: someValue"> </div>

注:你实际上不必把init和update这两个callbacks都定义,你能够只定义其中的任意一个。

 

update 回调

当管理的observable改变的时候,KO会调用你的update callback函数,而后传递如下参数:

element — 使用这个绑定的DOM元素

valueAccessor —JavaScript函数,经过valueAccessor()能够获得应用到这个绑定的model上的当前属性值。

allBindingsAccessor —JavaScript函数,经过allBindingsAccessor ()获得这个元素上全部model的属性值。

viewModel — 传递给ko.applyBindings使用的 view model参数,若是是模板内部的话,那这个参数就是传递给该模板的数据。

 

例如,你可能想经过 visible绑定来控制一个元素的可见性,可是你想让该元素在隐藏或者显示的时候加入动画效果。那你能够自定义本身的绑定来调用jQuery的slideUp/slideDown 函数:

ko.bindingHandlers.slideVisible = {
    update: function(element, valueAccessor, allBindingsAccessor) {
        // First get the latest data that we're bound to
        var value = valueAccessor(), allBindings = allBindingsAccessor();       

        // Next, whether or not the supplied model property is observable, get its current value
        var valueUnwrapped = ko.utils.unwrapObservable(value);

        // Grab some more data from another binding property
        var duration = allBindings.slideDuration || 400;

        // 400ms is default duration unless otherwise specified
    
        // Now manipulate the DOM element

        if (valueUnwrapped == true)
            $(element).slideDown(duration); // Make the element visible
        else
            $(element).slideUp(duration);   // Make the element invisible
    }
};

 

而后你能够像这样使用你的绑定:

<div data-bind="slideVisible: giftWrap, slideDuration:600">You have selected the option</div>
<label><input type="checkbox" data-bind="checked: giftWrap"/> Gift wrap</label>

<script type="text/javascript">
    var viewModel = {
        giftWrap: ko.observable(true)
    };
    ko.applyBindings(viewModel);
</script>

固然,看来可能代码不少,可是一旦你建立了自定义绑定,你就能够在不少地方重用它。

 

init 回调

Knockout在DOM元素使用自定义绑定的时候会调用你的init函数。init有两个重要的用途:

为DOM元素设置初始值

注册事件句柄,例如当用户点击或者编辑DOM元素的时候,你能够改变相关的observable值的状态。

 KO会传递和update回调函数同样的参数。

继续上面的例子,你能够像让slideVisible在页面第一次显示的时候设置该元素的状态(可是不使用任何动画效果),而只是让动画在之后改变的时候再执行。你能够这样来作:

ko.bindingHandlers.slideVisible = {
    init: function(element, valueAccessor) {
        var value = ko.utils.unwrapObservable(valueAccessor());
        // Get the current value of the current property we're bound to
        $(element).toggle(value);
        // jQuery will hide/show the element depending on whether "value" or true or false
    },

    update: function(element, valueAccessor, allBindingsAccessor) {
        // Leave as before
    }
};

这就是说giftWrap的初始值声明的是false(例如giftWrap: ko.observable(false)),而后让初始值会让关联的DIV隐藏,以后用户点击checkbox的时候会让元素显示出来。

 

DOM事件以后更新observable值

你已经值得了如何使用update回调,当observable值改变的时候,你能够更新相关的DOM元素。可是其它形式的事件怎么作呢?好比当用户对某个DOM元素有某些action操做的时候,你想更新相关的observable值。

你可使用init回调来注册一个事件句柄,这样能够改变相关的observable值,例如,

ko.bindingHandlers.hasFocus = {

    init: function (element, valueAccessor) {
        $(element).focus(function () {
            var value = valueAccessor();
            value(true);
        });

        $(element).blur(function () {
            var value = valueAccessor();
            value(false);
        });
    },

    update: function (element, valueAccessor) {
        var value = valueAccessor();
        if (ko.utils.unwrapObservable(value))
            element.focus();
        else
            element.blur();
    }
};

如今你能够经过hasFocus绑定来读取或者写入这个observable值了:

<p>Name: <input data-bind="hasFocus: editingName"/></p>
<!-- Showing that we can both read and write the focus state -->
<div data-bind="visible: editingName">You're editing the name</div>
<button data-bind="enable: !editingName(), click:function() { editingName(true) }">Edit name</button>

<script type="text/javascript">
    var viewModel = {
        editingName: ko.observable()
    };
    ko.applyBindings(viewModel);
</script>

 

Knockout应用开发指南 第六章:加载或保存JSON数据

2011-11-28 09:02 by 汤姆大叔, 12004 阅读, 3 评论, 收藏, 编辑

加载或保存JSON数据

Knockout能够实现很复杂的客户端交互,可是几乎全部的web应用程序都要和服务器端交换数据(至少为了本地存储须要序列化数据),交换数据最方便的就是使用JSON格式 – 大多数的Ajax应用程序也是使用这种格式。

 

加载或保存数据

Knockout不限制你用任何技术加载和保存数据。你可使用任何技术和服务器来交互。用的最多的是使用jQuery的Ajax帮助,例如:getJSON,post和ajax。你能够经过这些方法从服务器端获取数据:

$.getJSON("/some/url", function (data) {
    // Now use this data to update your view models,
    // and Knockout will update your UI automatically
})

… 或者向服务器端发送数据:

var data = /* Your data in JSON format - see below */;
$.post("/some/url", data, function(returnedData) {
    // This callback is executed if the post was successful  
})

或者,若是你不想用jQuery,你能够用任何其它的方式来读取或保存JSON数据。因此, Knockout须要你作的仅仅是:

    对于保存,让你的view model数据转换成简单的JSON格式,以方便使用上面的技术来保存数据。

    对于加载,更新你接收到的数据到你的view model上。

 

转化View Model数据到JSON格式

因为view model都是JavaScript对象,因此你须要使用标准的JSON序列化工具让转化view model为JSON格式。例如,可使用JSON.serialize()(新版本浏览器才支持的原生方法),或者使用json2.js类库。不过你的view model可能包括observables,依赖对象dependent observables和observable数组,有可能不能很好的序列化,你须要本身额外的处理一下数据。

 

为了使view model数据序列化方便(包括序列化observables等格式),Knockout提供了2个帮助函数:

    ko.toJS — 克隆你的view model对象,而且替换全部的observable 对象为当前的值,这样你能够获得一个干净的和Knockout无关的数据copy。

    ko.toJSON — 将view model对象转化成JSON字符串。原理就是:先调在view model上调用ko.toJS,而后调用浏览器原生的JSON 序列化器获得结果。注:一些老浏览器版本不支持原生的JSON 序列化器(例如:IE7和之前的版本),你须要引用json2.js类库。

 

声明一个view model:

var viewModel = {
    firstName: ko.observable("Bert"),
    lastName: ko.observable("Smith"),
    pets: ko.observableArray(["Cat", "Dog", "Fish"]),
    type: "Customer"
};

viewModel.hasALotOfPets = ko.dependentObservable(function () {
    return this.pets().length > 2
}, viewModel)

该view model包含observable类型的值,依赖类型的值dependent observable以及依赖数组observable array,和普通对象。你能够像以下代码同样使用ko.toJSON将此转化成服务器端使用的JSON 字符串:

var jsonData = ko.toJSON(viewModel);

// Result: jsonData is now a string equal to the following value
// '{"firstName":"Bert","lastName":"Smith","pets":["Cat","Dog","Fish"],"type":"Customer","hasALotOfPets":true}'

或者,序列化以前,你想获得JavaScript简单对象的话,直接使用像这样同样使用ko.toJS:

var plainJs = ko.toJS(viewModel);

// Result: plainJS is now a plain JavaScript object in which nothing is observable. It's just data.
// The object is equivalent to the following:
//   {
//      firstName: "Bert",
//      lastName: "Smith",
//      pets: ["Cat","Dog","Fish"],
//      type: "Customer",
//      hasALotOfPets: true
//   }

 

使用JSON更新View Model数据

若是你从服务器端获取数据而且更新到view model上,最简单的方式是本身实现。例如,

// Load and parse the JSON
var someJSON = /* Omitted: fetch it from the server however you want */;
var parsed = JSON.parse(someJSON);

// Update view model properties
viewModel.firstName(parsed.firstName);
viewModel.pets(parsed.pets);

不少状况下,最直接的方法就是最简单并且最灵活的方式。固然,若是你更新了view model的属性,Knockout会自动帮你更新相关的UI元素的。

 

不过,不少开发人员仍是喜欢使用一种好用而不是每次都写代码的方式来转化数据到view model上,尤为是view model有不少属性或者嵌套的数据结构的时候,这颇有用,由于能够节约不少代码量。knockout.mapping插件能够帮你作到这一点。

Knockout应用开发指南 第七章:Mapping插件

2011-11-29 09:08 by 汤姆大叔, 12908 阅读, 16 评论, 收藏, 编辑

Mapping插件

Knockout设计成容许你使用任何JavaScript对象做为view model。必须view model的一些属性是observable的,你可使用KO绑定他们到你的UI元素上,当这些observable值改变的时候,这些UI元素就会自动更新。

绝大多数程序都须要从服务器端获取数据,可是因为服务器不知道observable的概念是什么,它只支持简单的JavaScript对象(一般是 序列化之后的JSON),mapping插件可让你很方便地将简单JavaScript对象mapp到带有observable值的view model。你也能够本身写JavaScript代码将从服务器获取的数据来构建 view model,mapping插件只是一种很好的替代而已。

 下载

    Version 2.0 (最小版本8.6kb)

 

例子:手工mapping

显示当前服务器时间和你网站上的当前用户数。你应该使用以下的view model来表明你的这些信息:

var viewModel = {
    serverTime: ko.observable(),
    numUsers: ko.observable()
}

而后绑定view model到HTML元素上,以下:

The time on the server is: <span data-bind='text: serverTime'></span>
and <span data-bind='text: numUsers'></span> user(s) are connected.

因为view model属性是observable的,在他们变化的时候,KO会自动更新绑定的HTML元素。

接下来,从服务器获取最新的数据。或许每隔5秒你要调用一次Ajax请求(例如,使用jQuery的$.getJSON或$.ajax函授):

var data = getDataUsingAjax();          // Gets the data from the server

而后,服务器返回和下面类似的JSON数据:

{
    serverTime: '2010-01-07',
    numUsers: 3
}

最后,用这些数据更新你的view model(不使用mapping插件),代码以下:

// Every time data is received from the server:
viewModel.serverTime(data.serverTime);
viewModel.numUsers(data.numUsers);

为了使数据显示在页面上,全部的属性都要像这样写代码。若是你的数据结构很复杂的话(例如,包含子对象或者数组),那就维护起来就至关痛苦。mapping插件就是来让你让你的JavaScript简单对象(或JSON结构)转换成observable的view model的。

 

例子:使用ko.mapping

经过mapping插件建立view model,直接使用ko.mapping.fromJS函数来建立:

var viewModel = ko.mapping.fromJS(data);

它会自动将data里全部的属性建立成observable类型的属性。你能够经过ko.mapping.fromJS 函数按期从服务器获取数据,而后更新你的view model:

// Every time data is received from the server:
ko.mapping.fromJS(data, viewModel);

 

如何mapping?

    对象的全部属性都被转换成observable类型值,若是获取的对象的值改变了,就会更新这个observable类型的值.

    数组也被转换成了observable数组,若是服务器更新改变了数组的个数,mapping插件也会添加或者删除相应的item项,也会尽可能保持和原生JavaScript数组相同的order顺序。

 

Unmapping

若是你想让map过的对象转换成原来的JavaScript对象,使用以下方式:

var unmapped = ko.mapping.toJS(viewModel);

会建立一个unmapped对象,只包含你以前map过的对象属性,换句话说,你在view model上手工添加的属性或者函数都会被忽略的,惟一例外的是_destroy属性是能够unmapped回来的,由于你从ko.observableArray里destroy一个item项的时候会生成这个属性。 请参考“高级用户”小节如何配置使用。

 

与JSON字符串一块儿使用

若是你的Ajax调用返回的是JSON字符串(而不是反序列化后的JavaScript对象),你可使用ko.mapping.fromJSON函数来建立或者更新你的view model。用ko.mapping.toJSON实现unmap。

使用.from/toJSON函数处理JSON字符串和使用.from/toJS函数处理JS对象是等价的。

 

高级用法

有时候,在使用ko.mapping.fromJS的时候,可能有必要去使用mapping的高级用法来定义mapping的详细过程,之后定义了,之后再调用的时候就没必要再定义了。这里有一些情形,你可能须要使用这些option。

 

用法1:使用keys来使对象unique化

你有一个JavaScript对象,以下:

var data = {
    name: 'Scot',
    children: [
        { id: 1, name: 'Alicw' }
    ]
}

使用map插件,你能够将它map到view model上(没任何问题):

var viewModel = ko.mapping.fromJS(data);

如今,数据被更新成以下这样:

var data = {
    name: 'Scott',
    children: [
        { id: 1, name: 'Alice' }
    ]
}

这里发生了两件事:name从Scot变成了Scott,children[0].name从Alicw变成了Alice。你能够用以下代码更新view model:

ko.mapping.fromJS(data, viewModel);

因而,name像咱们指望的同样更新了,可是在children数组里,子项Alicw被删除而新项Alice被添加到数组里。这不是咱们所指望的,咱们指望的是只是把name从Alicw更新成Alice,不是替换整个item项。发生的缘由是,默认状况下mapping plugin插件只是简单地比较数组里的两个对象是否相等。 由于JavaScript里{ id : 1, name : 'Alicw' }和{ id : 1, name : 'Alice' }是不相等的,因此它认为喜欢将新项替换掉老项。

解决这个问题,你须要声明一个key让mapping插件使用,用来判断一个对象是新对象仍是旧对象。代码以下:

var mapping = {
    'children': {
        key: function (data) {
            return ko.utils.unwrapObservable(data.id);
        }
    }
}

var viewModel = ko.mapping.fromJS(data, mapping);

这样,每次map的时候,mapping插件都会检查数组项的id属性来判断这个数组项是须要合并的仍是全新replace的。

 

用法2:用create自定义对象的构造器

若是你想本身控制mapping,你也可使用create回调。使用回调可让你本身控制mapping。

举例,你有一个像这样的JavaScript对象:

var data = {
    name: 'Graham',
    children: [
        { id: 1, name: 'Lisa' }
    ]
}

若是你想本身map children 数组,你能够这样声明:

var mapping = {
    'children': {
        create: function (options) {
            return new myChildModel(options.data);
        }
    }
}

var viewModel = ko.mapping.fromJS(data, mapping);

支持create回调的options参数是一个JavaScript对象,包含以下:

data: JavaScript对象,包含child用到的数据

parent:child对象所属的父对象或者数组

 固然,在内部的create回调里,你也能够再次调用ko.mapping.fromJS。一个例子就是:若是你想让初始的JavaScript对象带有额外的依赖属性dependent observables:

var myChildModel = function (data) {
    ko.mapping.fromJS(data, {}, this);

    this.nameLength = ko.dependentObservable(function () {
        return this.name().length;
    }, this);
}

 

用法3:用update自定义对象的updating

你也可使用update 回调来自定义一个对象如何更新。它接受一个须要替代的对象以及和create 回调同样的options参数,你应该return更新后的值。

update 回调使用的options参数是一个JavaScript对象,包含以下内容:

data:JavaScript对象,包含child用到的数据

parent:child对象所属的父对象或者数组

observable:若是属性是observable的,这将会写入到实际的observable里

 例子,在数据显示以前,在新数据后面附加额外的字符串:

var data = {
    name: 'Graham',
}

var mapping = {
    'name': {
        update: function(options) {
            return options.data + 'foo!';
        }
    }
}

var viewModel = ko.mapping.fromJS(data, mapping);
alert(viewModel.name());

alert的结果是:Grahamfoo!。

 

用法4:使用ignore忽略不须要map的属性

若是在map的时候,你想忽略一些属性,你可使用ignore累声明须要忽略的属性名称集合:

var mapping = {
    'ignore': ["propertyToIgnore", "alsoIgnoreThis"]
}

var viewModel = ko.mapping.fromJS(data, mapping);

你声明的忽略数组被编译到默认的ignore数组里。你能够像下面代码同样来维护它:

var oldOptions = ko.mapping.defaultOptions().ignore;
ko.mapping.defaultOptions().ignore = ["alwaysIgnoreThis"];

 

用法5:使用include声明须要map的属性

默认状况下,当map你的view model回到JS对象是时候,只map原始view model里拥有的属性(除了例外的_destroy属性),不过,你可使用include参数来定制:

var mapping = {
    'include': ["propertyToInclude", "alsoIncludeThis"]
}

var viewModel = ko.mapping.fromJS(data, mapping);

你声明的include数组被编译到默认的include数组里,默认只有_destroy。 你能够像这样来维护:

var oldOptions = ko.mapping.defaultOptions().include;
ko.mapping.defaultOptions().include = ["alwaysIncludeThis"];

 

用法6:使用copy来复制属性

默认状况下,map的时候是把全部的值都转换成observable的,若是你只是想copy属性值而不是替换成observable的,你能够将属性名称添加到copy数组:

var mapping = {
    'copy': ["propertyToCopy"]
}

var viewModel = ko.mapping.fromJS(data, mapping);

你声明的copy数组被编译到默认的copy数组里,默认值是空。你能够像这样来维护:

var oldOptions = ko.mapping.defaultOptions().copy;
ko.mapping.defaultOptions().copy = ["alwaysCopyThis"];

 

用法7:Specifying the update target

在上面的例子,若是你想再一个class内map,你可使用第三个参数做为操做的目标,例如:

ko.mapping.fromJS(data, {}, someObject); // overwrites properties on someObject

因此,若是你想map一个JavaScript对象到this上,你能够这样声明:

ko.mapping.fromJS(data, {}, this);

 

从多数据源map

你能够经过屡次使用ko.mapping.fromJS 来将多个JS对象的数据源map到一块儿,例如:

var viewModel = ko.mapping.fromJS(alice, aliceMappingOptions);
ko.mapping.fromJS(bob, bobMappingOptions, viewModel);

你声明的mapping选项option在每次调用的时候都会合并。

 

Map之后的observable数组

map插件map之后生产的observable数组,带有几个额外的函数来处理带有keys的mapping:

mappedRemove

mappedRemoveAll

mappedDestroy

mappedDestroyAll

mappedIndexOf

 它们是和ko.observableArray里的函数等价的,不一样是他们经过key来处理对象。例如:

var obj = [
    { id: 1 },
    { id: 2 }
]

var result = ko.mapping.fromJS(obj, {
    key: function (item) {
        return ko.utils.unwrapObservable(item.id);
    }
});

result.mappedRemove({ id: 2 });

map过的observable数组,除了上面的函数还支持一个mappedCreate函数:

var newItem = result.mappedCreate({ id: 3 });

首先会检查key(id=3)在数组里是否存在(若是存在则抛出异常),而后,若是有create和 update回调的话会调用他们,最后建立一个新对象,并将新对象添加到数组而后返回该新对象。

Knockout应用开发指南 第八章:简单应用举例(1)

2011-11-30 09:04 by 汤姆大叔, 11697 阅读, 5 评论, 收藏, 编辑

本章展现的4个例子主要是利用了Knockout的基本语法特性,让你们感觉到使用Kncokout的快感。

1   Hello world

这个例子里,2个输入框都被绑定到data model上的observable变量上。“full name”显示的是一个dependent observable,它的值是前面2个输入框的值合并一块儿的结果。

 

 不管哪一个输入框更新,都会看到“full name” 显示结果都会自动更新。查看HTML源代码能够看到咱们不须要声明onchange事件。Knockout知道何时该更新UI。

 

代码: View

<p>First name: <input data-bind="value: firstName"/></p>
<p>Last name: <input data-bind="value: lastName"/></p>
<h2>Hello, <span data-bind="text: fullName"> </span>!</h2>

代码: View model

// 这里是声明的view model

var viewModel = {
    firstName: ko.observable("Planet"),
    lastName: ko.observable("Earth")
};

viewModel.fullName = ko.dependentObservable(function () {
    // Knockout tracks dependencies automatically.
    //It knows that fullName depends on firstName and lastName,          
    //because these get called when evaluating fullName.
    return viewModel.firstName() + " " + viewModel.lastName();
});

ko.applyBindings(viewModel); // This makes Knockout get to work

 

2   Click counter

这个例子展现的建立一个view model而且绑定各类绑定到HTML元素标记上,以便展现和修改view model的状态。

Knockout根据依赖项。在内部,hasClickedTooManyTimes在numberOfClicks上有个订阅,以便当numberOfClicks改变的时候,强制hasClickedTooManyTimes从新执行。类似的,UI界面上多个地方引用hasClickedTooManyTimes,因此当hasClickedTooManyTimes 改变的时候,也讲致使UI界面更新。

不须要手工声明或者订阅这些subscription订阅,他们由KO框架本身建立和销毁。参考以下代码实现:

 

 

代码: View

<div>You've clicked <span data-bind="text: numberOfClicks">&nbsp;</span> times</div>

<button data-bind="click: registerClick, enable: !hasClickedTooManyTimes()">Click me</button>

<div data-bind="visible: hasClickedTooManyTimes">
    That's too many clicks! Please stop before you wear out your fingers.
    <button data-bind="click: function() { numberOfClicks(0) }">Reset clicks</button>
</div>

代码: View model

var clickCounterViewModel = function () {
    this.numberOfClicks = ko.observable(0);

    this.registerClick = function () {
        this.numberOfClicks(this.numberOfClicks() + 1);
    }

    this.hasClickedTooManyTimes = ko.dependentObservable(function () {
        return this.numberOfClicks() >= 3;
    }, this);
};

ko.applyBindings(new clickCounterViewModel());

 

3   Simple list

这个例子展现的是绑定到数组上。

注意到,只有当你在输入框里输入一些值的时候,Add按钮才可用。参考下面的HTML代码是如何使用enable 绑定。

 

 

代码: View

<form data-bind="submit: addItem">
    New item:
    <input data-bind='value: itemToAdd, valueUpdate: "afterkeydown"' />
    <button type="submit" data-bind="enable: itemToAdd().length > 0">Add</button>
    <p>Your items:</p>
    <select multiple="multiple" width="50" data-bind="options: items"> </select>
</form>

代码: View model

var viewModel = {};
viewModel.items = ko.observableArray(["Alpha", "Beta", "Gamma"]);
viewModel.itemToAdd = ko.observable("");
viewModel.addItem = function () {
    if (viewModel.itemToAdd() != "") {
        viewModel.items.push(viewModel.itemToAdd());
        // Adds the item. Writing to the "items" observableArray causes any associated UI to update.

        viewModel.itemToAdd("");                 
        // Clears the text box, because it's bound to the "itemToAdd" observable
    }
}

ko.applyBindings(viewModel);

 

4   Better list

这个例子是在上个例子的基础上添加remove item功能(multi-selection)和排序功能。 “remove”和“sort”按钮在不能用的时候会变成disabled状态(例如,没有足够的item来排序)。

参考HTML代码是如何实现这些功能的,另外这个例子也展现了如何使用匿名函数来实现排序。

 

 

代码: View

<form data-bind="submit:addItem">
    Add item: <input type="text" data-bind='value:itemToAdd, valueUpdate: "afterkeydown"' />
    <button type="submit" data-bind="enable: itemToAdd().length > 0">Add</button>
</form>

<p>Your values:</p>
<select multiple="multiple" height="5" data-bind="options:allItems, selectedOptions:selectedItems"> </select>

<div>
    <button data-bind="click: removeSelected, enable: selectedItems().length > 0">Remove</button>
    <button data-bind="click: function() { allItems.sort() }, enable: allItems().length > 1">Sort</button>
</div>

代码: View model

// In this example, betterListModel is a class, and the view model is an instance of it.

// See simpleList.html for an example of how to construct a view model without defining a class for it. Either technique works fine.

var betterListModel = function () {
    this.itemToAdd = new ko.observable("");
    this.allItems = new ko.observableArray(["Fries", "Eggs Benedict", "Ham", "Cheese"]);

// Initial items

this.selectedItems = new ko.observableArray(["Ham"]);                              

// Initial selection

    this.addItem = function () {
        if ((this.itemToAdd() != "") && (this.allItems.indexOf(this.itemToAdd()) < 0))
    // Prevent blanks and duplicates
        this.allItems.push(this.itemToAdd());
        this.itemToAdd(""); // Clear the text box
    }

    this.removeSelected = function () {
        this.allItems.removeAll(this.selectedItems());
        this.selectedItems([]); // Clear selection
    }
};

ko.applyBindings(new betterListModel());

 

Knockout应用开发指南 第八章:简单应用举例(2)

2011-12-01 09:47 by 汤姆大叔, 8437 阅读, 8 评论, 收藏, 编辑

5   Control types

这个例子,对view model没有什么特殊的展现,只是展现如何绑定到各类元素上(例如,select, radio button等)。

 

代码: View

View Code

 

代码: 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">&nbsp;</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

 

代码: View

<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 能够看到效果。

 

 

 

代码: View

<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);

 

Knockout应用开发指南 第九章:高级应用举例

2011-12-02 15:04 by 汤姆大叔, 12079 阅读, 9 评论, 收藏, 编辑

1   Contacts editor

这个例子和微软为演示jQuery Data Linking Proposal例子提供的例子同样的提供的,咱们能够看看Knockout实现是难了仍是容易了。

代码量的多少不重要(尽快Knockout 的实现很简洁),重要的看起来是否容易理解且可读。查看HTML源代码,看看如何实现的view model以及绑定的。

 

 

代码: View

View Code

 

代码: View model

View Code

 

2   Editable grid

该例是使用“foreach”绑定为数组里的每一项来render到 template上。好处(相对于模板内部使用for循环)是当你添加或者删除item项的时候,Knockout不须要从新render – 只须要render新的item项。就是说UI上其它控件的状态(好比验证状态)不会丢失。

如何一步一步构建这个例子并集成ASP.NET MVC,请参阅此贴

 

 

代码: View

View Code

 

代码: View model

View Code

 

3   Shopping cart screen

这个例子展现的是依赖监控属性(dependent observable)怎么样链在一块儿。每一个cart对象都有一个dependentObservable对象去计算本身的subtotal,这些又被一 个进一步的dependentObservable对象依赖计算总的价格。当改变数据的时候,整个链上的依赖监控属性都会改变,全部相关的UI元素也会被 更新。

这个例子也展现了如何建立联动的下拉菜单。

 

 

代码: View

View Code

 

代码: View model

View Code

 

4   Twitter client

这是一个复杂的例子,展现了几乎全部Knockout特性来构建一个富客户端。

用户数据存在一个JavaScript模型里,经过模板来展现。就是说咱们能够经过清理用户列表里的数据来达到隐藏用户信息的目的,而不须要手动去隐藏DOM元素。

按钮将根据他们是否可操做来自动变成enabled或disabled状态。例如,有一个叫hasUnsavedChanges的依赖监控属性(dependentObservable)控制这“Save”按钮的enabled状态。

能够很是方便地从外部JSON服务获取数据,并集成到view model里,而后显示在页面上。

 

 

代码: View

 

 

<div class="loadingIndicator">
    Loading...</div>
<div class="configuration">
    <div class="listChooser">
        <button data-bind='click: deleteList, enable: editingList.name'>
            Delete</button>
        <button data-bind='click: saveChanges, enable: hasUnsavedChanges'>
            Save</button>
        <select data-bind='options: savedLists, optionsValue: "name", value: editingList.name'>
        </select>
    </div>
    <p>
        Currently viewing <span data-bind="text: editingList.userNames().length">&nbsp;</span>
        user(s):</p>
    <div class="currentUsers" data-bind='template: { name: "usersTemplate", data: editingList }'>
    </div>
    <form data-bind="submit: addUser">
    <label>
        Add user:</label>
    <input data-bind='value: userNameToAdd, valueUpdate: "keyup", css: { invalid: !userNameToAddIsValid() }' />
    <button type="submit" data-bind='enable: userNameToAddIsValid() && userNameToAdd() != ""'>
        Add</button>
    </form>
</div>
<div class="tweets" data-bind='template: { name: "tweetsTemplate", data: currentTweets }'>
</div>
<script type="text/html" id="tweetsTemplate">
    <table width="100%">
        {{each $data}}
            <tr>
                <td><img src="${ profile_image_url }"/></td>
                <td>
                    <a class="twitterUser" href="http://twitter.com/${ from_user }">${ from_user }</a>
                    ${ text }
                    <div class="tweetInfo">${ created_at }</div>
</td>
</tr>
        {{/each}}
</table>
</script>
<script type="text/html" id="usersTemplate">
    <ul>
        {{each(i, userName) userNames()}}
            <li><button data-bind="click: function() { userNames.remove(userName) }">Remove</button> <div>${ userName }</div></li>
        {{/each}}
</ul>
</script>

 

 

代码: View model

 

 

// The view model holds all the state we're working with. It also has methods that can edit it, and it uses
// dependentObservables to compute more state in terms of the underlying data
// --
// The view (i.e., the HTML UI) binds to this using data-bind attributes, so it always stays up-to-date with
// the view model, even though the view model does not know or care about any view that binds to it

var viewModel = {
    savedLists: ko.observableArray([
        { name: "Celebrities", userNames: ['JohnCleese', 'MCHammer', 'StephenFry', 'algore', 'StevenSanderson'] },
        { name: "Microsoft people", userNames: ['BillGates', 'shanselman', 'haacked', 'ScottGu'] },
        { name: "Tech pundits", userNames: ['Scobleizer', 'LeoLaporte', 'techcrunch', 'BoingBoing', 'timoreilly', 'codinghorror'] }
    ]),

    editingList: {
        name: ko.observable("Tech pundits"),
        userNames: ko.observableArray()
    },

    userNameToAdd: ko.observable(""),
    currentTweets: ko.observableArray([])
};

viewModel.findSavedList = function (name) {
    var lists = this.savedLists();

    for (var i = 0; i < lists.length; i++)
        if (lists[i].name === name)
            return lists[i];
};

// Methods
viewModel.addUser = function () {
    if (this.userNameToAdd() && this.userNameToAddIsValid()) {
        this.editingList.userNames.push(this.userNameToAdd());
        this.userNameToAdd("");
    }
}

viewModel.saveChanges = function () {
    var saveAs = prompt("Save as", this.editingList.name());

    if (saveAs) {
        var dataToSave = this.editingList.userNames().slice(0);
        var existingSavedList = this.findSavedList(saveAs);
        if (existingSavedList)
            existingSavedList.userNames = dataToSave; // Overwrite existing list
        else
            this.savedLists.push({ name: saveAs, userNames: dataToSave }); // Add new list

        this.editingList.name(saveAs);
    }
}

viewModel.deleteList = function () {
    var nameToDelete = this.editingList.name();
    var savedListsExceptOneToDelete = $.grep(this.savedLists(), function (list) { return list.name != nameToDelete });
    this.editingList.name(savedListsExceptOneToDelete.length == 0 ? null : savedListsExceptOneToDelete[0].name);
    this.savedLists(savedListsExceptOneToDelete);
};

ko.dependentObservable(function () {
    // Observe viewModel.editingList.name(), so when it changes (i.e., user selects a different list) we know to copy the saved list into the editing list
    var savedList = viewModel.findSavedList(viewModel.editingList.name());

    if (savedList) {
        var userNamesCopy = savedList.userNames.slice(0);
        viewModel.editingList.userNames(userNamesCopy);
    } else
        viewModel.editingList.userNames([]);
});

viewModel.hasUnsavedChanges = ko.dependentObservable(function () {
    if (!this.editingList.name())
        return this.editingList.userNames().length > 0;

    var savedData = this.findSavedList(this.editingList.name()).userNames;
    var editingData = this.editingList.userNames();
    return savedData.join("|") != editingData.join("|");
}, viewModel);

viewModel.userNameToAddIsValid = ko.dependentObservable(function () {
    return (this.userNameToAdd() == "") || (this.userNameToAdd().match(/^\s*[a-zA-Z0-9_]{1,15}\s*$/) != null);
}, viewModel);

// The active user tweets are (asynchronously) computed from editingList.userNames
ko.dependentObservable(function () {
    twitterApi.getTweetsForUsers(this.editingList.userNames(), function (data) { viewModel.currentTweets(data) })
}, viewModel);

ko.applyBindings(viewModel);

// Using jQuery for Ajax loading indicator - nothing to do with Knockout
$(".loadingIndicator").ajaxStart(function () { $(this).fadeIn(); })
                      .ajaxComplete(function () { $(this).fadeOut(); });

 

Knockout应用开发指南 第十章:更多信息(完结篇)

2011-12-05 09:16 by 汤姆大叔, 7762 阅读, 2 评论, 收藏, 编辑

1   浏览器支持

Knockout在以下浏览器经过测试:

Mozilla Firefox 2.0+(最新测试版本:3.6.8)

Google Chrome(经过Windows and Mac 下的version 5测试;其它低版本也该能够工做)

Microsoft Internet Explorer 6, 7, 8

Apple Safari(Windows下的Safari 5测试,Mac OS X下的 Safari 3.1.2测试,以及iPhone下的Safari for iOS 4测试;高低版本应该均可以工做)

Opera 10 for Windows

Knockout应该在以上这个浏览器的各版本上工做,可是因为太多版本,没有逐一测试。最新测试结果显示, Knockout在以下浏览器也是能够工做的(尽管没有对每一个版本逐一测试):

    Opera Mini

    Google Android OS browser (OS version 2.2)

测试Knockout可否在一个新平台或浏览器下工做,只须要下载源代码,而后在该浏览器里运行里面的/spec/runner.html文件便可测试。这个文件能够验证超过100个行为特性,若是有问题则会生成报表。上述浏览器的测试结果都应该是100%经过。

2      寻求帮助

有任何问题,你能够在Google group进去寻求帮助。

地址:http://groups.google.com/group/knockoutjs

3      更多教程和例子

这里有更多使用Knockout和其它相关技术的页面和例子:

    Knock Me Out — Ryan Niemeyer的博客,包括KnockoutJS和相关技术的不少好创意、想法和讨论

    Editing a variable-length list, Knockout-style — Steve Sanderson展现的在ASP.NET MVC下使用Knockout的好处

    Knockout+WebSockets — Carl Hörberg使用Knockout,Sinatra,SQLite和WebSockets实现的实时讨论版

    Knockout – quick asp.net mvc sample — Steve Gentile提供的另一篇博客, 关于ASP.NET MVC 下如何使用Knockout

    Log4Play: Log4j Live Streaming with Play Framework, Knockout.js and WebSockets — Felipe Oliveira关于使用KO和WebSockets建立实时server log功能

    Wiki – 攻略 — 网友贡献的攻略和例子

    Wiki – 插件 — 网友贡献的各类插件列表

 

此文列表正在收集中,若是你想让你的帖子链接放在这里,请告诉我。

用Javascript评估用户输入密码的强度(Knockout版)

2011-11-27 10:40 by 汤姆大叔, 8090 阅读, 10 评论, 收藏, 编辑

早上看到博友6点多发的一篇关于密码强度的文章(链接),甚是感动(周末大早上还来发文)。

咱们来看看若是使用Knockout更简单的来实现密码强度的验证。

原有代码请查看:

 

 

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <title></title>
</head>
<body>
    <script type="text/javascript">
        //CharMode函数
function CharMode(iN) {
            if (iN >=48&& iN <=57) //数字
return1;
            if (iN >=65&& iN <=90) //大写字母
return2;
            if (iN >=97&& iN <=122) //小写
return4;
            else
                return8; //特殊字符
        }

        //bitTotal函数
function bitTotal(num) {
            modes =0;
            for (i =0; i <4; i++) {
                if (num &1) modes++;
                num >>>=1;
            }
            return modes;
        }

        //checkStrong函数
function checkStrong(sPW) {
            if (sPW.length <=4)
                return0; //密码过短
            Modes =0;
            for (i =0; i < sPW.length; i++) {
                Modes |= CharMode(sPW.charCodeAt(i));
            }
            return bitTotal(Modes);
        }


        //pwStrength函数
function pwStrength(pwd) {
            O_color ="#eeeeee";
            L_color ="#FF0000";
            M_color ="#FF9900";
            H_color ="#33CC00";
            if (pwd ==null|| pwd =='') {
                Lcolor = Mcolor = Hcolor = O_color;
            } else {
                S_level = checkStrong(pwd);
                switch (S_level) {
                    case0:
                        Lcolor = Mcolor = Hcolor = O_color;
                    case1:
                        Lcolor = L_color;
                        Mcolor = Hcolor = O_color;
                        break;
                    case2:
                        Lcolor = Mcolor = M_color;
                        Hcolor = O_color;
                        break;
                    default:
                        Lcolor = Mcolor = Hcolor = H_color;
                }

                document.getElementById("strength_L").style.background = Lcolor;
                document.getElementById("strength_M").style.background = Mcolor;
                document.getElementById("strength_H").style.background = Hcolor;
                return;
            }
        } </script>
    <form name="form1" action="">
    输入密码:<input type="password" size="10" onkeyup="pwStrength(this.value)" onblur="pwStrength(this.value)">
    <br>
    密码强度:
    <table width="217" border="1" cellspacing="0" cellpadding="1" bordercolor="#cccccc"
        height="23" style='display: inline'>
        <tr align="center" bgcolor="#eeeeee">
            <td width="33%" id="strength_L">
                弱
            </td>
            <td width="33%" id="strength_M">
                中
            </td>
            <td width="33%" id="strength_H">
                强
            </td>
        </tr>
    </table>
    </form>
</body>
</html>

 

 

首先咱们来改善一下上面博友的验证函数为以下代码:

var Page = Page || {};
Page.Utility = Page.Utility || {};
Page.Utility.Registration = Page.Utility.Registration || {};

//获取密码强度
Page.Utility.Registration.getPasswordLevel = function (password) {
    if (password == null || password == '')
        return 0;

    if (password.length <= 4)
        return 0; //密码过短

    var Modes = 0;
    for (i = 0; i < password.length; i++) {
        Modes |= CharMode(password.charCodeAt(i));
    }
    return bitTotal(Modes);

    //CharMode函数
    function CharMode(iN) {
        if (iN >= 48 && iN <= 57) //数字
            return 1;
        if (iN >= 65 && iN <= 90) //大写字母
            return 2;
        if (iN >= 97 && iN <= 122) //小写
            return 4;
        else
            return 8; //特殊字符
    }

    //bitTotal函数
    function bitTotal(num) {
        modes = 0;
        for (i = 0; i < 4; i++) {
            if (num & 1) modes++;
            num >>>= 1;
        }
        return modes;
    }
};

 

而后来建立View Model,可是引用Knockout以前,咱们首先要引用Knockout的Js类库(具体介绍请查看Knockout应用开发指南的系列教程)
View model代码以下:

var viewModel = {
    Password: ko.observable(""),
    Ocolor: "#eeeeee"
};

对于密码强度以及颜色的值依赖于密码字符串的值,因此咱们须要为他们声明依赖属性,代码以下:

viewModel.PasswordLevel = ko.dependentObservable(function () {
    return Page.Utility.Registration.getPasswordLevel(this.Password());
}, viewModel);

viewModel.Lcolor = ko.dependentObservable(function () {
    //根据密码强度判断第一个格显示的背景色
    return this.PasswordLevel() == 0 ? this.Ocolor : (this.PasswordLevel() == 1 ? "#FF0000" : (this.PasswordLevel() == 2 ? "#FF9900" : "#33CC00"))
}, viewModel);

viewModel.Mcolor = ko.dependentObservable(function () {
    //根据密码强度判断第二个格显示的背景色
    return this.PasswordLevel() < 2 ? this.Ocolor : (this.PasswordLevel() == 2 ? "#FF9900" : "#33CC00")
}, viewModel);

viewModel.Hcolor = ko.dependentObservable(function () {
    //根据密码强度判断第三个格显示的背景色
    return this.PasswordLevel() < 3 ? this.Ocolor : "#33CC00"
}, viewModel);

而后使用applyBindings方法将view model绑定到该页面,你可使用jQuery的ready函数来执行该绑定代码,也能够在页面最下方执行绑定代码,咱们这里使用了jQuery,代码以下:

$((function () {
    ko.applyBindings(viewModel);
}));

 

最后,咱们再看看这些值怎么动态绑定到HTML元素上的,请查看以下代码(其中使用了afterkeydown代替了onKeyUp和onBlur):

<form name="form1" action="">
输入密码:
<input type="text" size="10" data-bind="value:Password, valueUpdate: 'afterkeydown'">
<br>
密码强度:
<table width="217" border="1" cellspacing="0" cellpadding="1" bordercolor="#cccccc"
    height="23" style='display: inline'>
    <tr align="center" bgcolor="#eeeeee">
        <td width="50"data-bind="style: { backgroundColor: Lcolor }">弱</td>
        <td width="50"data-bind="style: { backgroundColor: Mcolor }">中</td>
        <td width="50"data-bind="style: { backgroundColor: Hcolor }">强</td>
    </tr>
</table>
</form>

而后就OK,运行代码查看,如出一辙的功能展现出来了。

若是去掉为验证而改善的代码,总代码确定是比原有的方式少的。

 

完整版代码以下:

 

 

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd"> <html> <head>     <script type="text/javascript" src="http://knockoutjs.com/js/jquery-1.4.2.min.js"></script>     <script type="text/javascript" src="http://knockoutjs.com/js/jquery.tmpl.js"></script>     <script type="text/javascript" src="http://knockoutjs.com/js/knockout-1.2.1.js"></script> </head> <body>     <script type="text/javascript">         var Page = Page || {};         Page.Utility = Page.Utility || {};         Page.Utility.Registration = Page.Utility.Registration || {};         //获取密码强度         Page.Utility.Registration.getPasswordLevel =function (password) {             if (password ==null|| password =='')                 return0;             if (password.length <=4)                 return0; //密码过短             var Modes =0;             for (i =0; i < password.length; i++) {                 Modes |= CharMode(password.charCodeAt(i));             }             return bitTotal(Modes);             //CharMode函数 function CharMode(iN) {                 if (iN >=48&& iN <=57) //数字 return1;                 if (iN >=65&& iN <=90) //大写字母 return2;                 if (iN >=97&& iN <=122) //小写 return4;                 else                     return8; //特殊字符             }             //bitTotal函数 function bitTotal(num) {                 modes =0;                 for (i =0; i <4; i++) {                     if (num &1) modes++;                     num >>>=1;                 }                 return modes;             }         };         var viewModel = {             Password: ko.observable(""),             Ocolor: "#eeeeee"         };         viewModel.PasswordLevel = ko.dependentObservable(function () {             return Page.Utility.Registration.getPasswordLevel(this.Password());         }, viewModel);         viewModel.Lcolor = ko.dependentObservable(function () {             //根据密码强度判断第一个格显示的背景色 returnthis.PasswordLevel() ==0?this.Ocolor : (this.PasswordLevel() ==1?"#FF0000" : (this.PasswordLevel() ==2?"#FF9900" : "#33CC00"))         }, viewModel);         viewModel.Mcolor = ko.dependentObservable(function () {             //根据密码强度判断第二个格显示的背景色 returnthis.PasswordLevel() <2?this.Ocolor : (this.PasswordLevel() ==2?"#FF9900" : "#33CC00")         }, viewModel);         viewModel.Hcolor = ko.dependentObservable(function () {             //根据密码强度判断第二个格显示的背景色 returnthis.PasswordLevel() <3?this.Ocolor : "#33CC00"         }, viewModel);         $((function () {             ko.applyBindings(viewModel);         }));            </script>     <form name="form1" action="">     输入密码:<input type="text" size="10" data-bind="value:Password, valueUpdate: 'afterkeydown'">     <br>     密码强度:     <table width="217" border="1" cellspacing="0" cellpadding="1" bordercolor="#cccccc"         height="23" style='display: inline'>         <tr align="center" bgcolor="#eeeeee">             <td width="50" id="strength_L" data-bind="style: { backgroundColor: Lcolor }">                 弱             </td>             <td width="50" id="strength_M" data-bind="style: { backgroundColor: Mcolor }">                 中             </td>             <td width="50" id="strength_H" data-bind="style: { backgroundColor: Hcolor }">                 强             </td>         </tr>     </table>     </form> </body> </html>

相关文章
相关标签/搜索