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:
}

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

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:

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: , name: 'Alicw' }
]
}

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

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

现在,数据被更新成如下这样:

var data = {
name: 'Scott',
children: [
{ id: , 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: , 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: },
{ id: }
] var result = ko.mapping.fromJS(obj, {
key: function (item) {
return ko.utils.unwrapObservable(item.id);
}
}); result.mappedRemove({ id: });

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

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

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

下载

Version 2.0 (最小版本8.6kb)

第七章:Mapping插件的更多相关文章

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

    原文:Knockout应用开发指南 第七章:Mapping插件 Mapping插件 Knockout设计成允许你使用任何JavaScript对象作为view model.必须view model的一些 ...

  2. IDEA第七章----插件

    idea的另一个可爱之处,就是它的强大的插件,下面我以CodeGlance插件为例,这个可以快速定位代码 第一节:安装插件 ● All plugins 显示所有插件. ● Enabled 显示当前所有 ...

  3. Gradle 1.12用户指南翻译——第三十九章. IDEA 插件

    本文由CSDN博客万一博主翻译,其他章节的翻译请参见: http://blog.csdn.net/column/details/gradle-translation.html 翻译项目请关注Githu ...

  4. 2017.2.12 开涛shiro教程-第七章-与Web集成

    2017.2.9 开涛shiro教程-第七章-与Web集成(一) 原博客地址:http://jinnianshilongnian.iteye.com/blog/2018398 根据下载的pdf学习. ...

  5. [书籍翻译] 《JavaScript并发编程》第七章 抽取并发逻辑

    本文是我翻译<JavaScript Concurrency>书籍的第七章 抽取并发逻辑,该书主要以Promises.Generator.Web workers等技术来讲解JavaScrip ...

  6. 精通Web Analytics 2.0 (9) 第七章:失败更快:爆发测试与实验的能量

    精通Web Analytics 2.0 : 用户中心科学与在线统计艺术 第七章:失败更快:爆发测试与实验的能量 欢迎来到实验和测试这个棒极了的世界! 如果Web拥有一个超越所有其他渠道的巨大优势,它就 ...

  7. 《Entity Framework 6 Recipes》中文翻译系列 (38) ------ 第七章 使用对象服务之动态创建连接字符串和从数据库读取模型

    翻译的初衷以及为什么选择<Entity Framework 6 Recipes>来学习,请看本系列开篇 第七章 使用对象服务 本章篇幅适中,对真实应用中的常见问题提供了切实可行的解决方案. ...

  8. 《Entity Framework 6 Recipes》中文翻译系列 (41) ------ 第七章 使用对象服务之标识关系中使用依赖实体与异步查询保存

    翻译的初衷以及为什么选择<Entity Framework 6 Recipes>来学习,请看本系列开篇 7-7  标识关系中使用依赖实体 问题 你想在标识关系中插入,更新和删除一个依赖实体 ...

  9. KnockoutJS 3.X API 第八章 映射(mapping)插件

    Knockout旨在允许您将任意JavaScript对象用作视图模型. 只要一些视图模型的属性是observables,您可以使用KO将它们绑定到您的UI,并且UI将在可观察属性更改时自动更新. 大多 ...

随机推荐

  1. Bash to check SSL cert expired

    Code like this, You can send out a email to notice $ cat urls.txt www.baidu.com $ cat cert_chk.sh #! ...

  2. 驱动学习3.1:获取zynqled的物理地址

    自己想要打印EMIO管脚的物理地址,在SDK提供的头文件中加入printf是无法打印的,基于此 我将需要打印地址的几个函数提取出来,放在main函数中,然后在里面加入printf打印这些用户管脚的地址 ...

  3. Codeforces Beta Round #34 (Div. 2) E. Collisions

    E. Collisions time limit per test 2 seconds memory limit per test 256 megabytes input standard input ...

  4. What are the advantages of different classification algorithms?

    What are the advantages of different classification algorithms? For instance, if we have large train ...

  5. java中集合去重1

    //TreeMap 处理 处理后 得到的值 按拼音排序 TreeMap map = new TreeMap(new MaterialComparator()); map.put("东莞&qu ...

  6. 去除UITableView多余的seperator

    UIView *v = [[UIView alloc] initWithFrame:CGRectZero]; [tableView setTableFooterView:v]; [v release] ...

  7. 关于HttpWebRequest发生服务器协议冲突的解决办法

    WinForm下的app.config文件中添加: <system.net>    <settings>      <httpWebRequest useUnsafeHe ...

  8. 面向过程编程(OPP) 和面向对象编程(OOP)的关系

    面向过程编程(OPP) 和面向对象编程(OOP)的关系 原文链接:http://blog.csdn.net/phphot/article/details/3985480 关于面向过程的编程(OPP)和 ...

  9. hash算法搜索获得api函数地址的实现,"kernel32.dll", "CreateThread"

    我们一般要获得一个函数的地址,通常采用的是明文,例如定义一个api函数字符串"MessageBoxA",然后在GetProcAddress函数中一个字节一个字节进行比较.这样弊端很 ...

  10. C++之 extern C的作用详解

    extern "C"的主要作用就是为了能够正确实现C++代码调用其他C语言代码.加上extern "C"后,会指示编译器这部分代码按C语言的进行编译,而不是C+ ...