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

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

大多数应用程序需要从后端服务器获取数据。 由于服务器没有任何可观察的概念,它只提供一个纯JavaScript对象(通常序列化为JSON)。 映射插件提供了一种简单的方法来将该简单的JavaScript对象映射到具有适当的observables的视图模型中。 这是替代手动编写自己的JavaScript代码,根据您从服务器获取的一些数据构建视图模型。

下载映射插件(你需要翻个墙)

 

示例:不带ko.mapping插件的手动映射

您要显示当前服务器时间和您的网页上的用户数。 您可以使用以下视图模型表示此信息:

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

您可以将此视图模型绑定到一些HTML元素,如下所示:

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

由于视图模型属性是可观察的,KO将在这些属性更改时自动更新HTML元素。

接下来,您要从服务器获取最新数据。 每5秒您可以发出一个Ajax请求(例如,使用jQuery的$ .getJSON或$ .ajax函数):

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

服务器可能返回类似于以下内容的JSON数据:

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

最后,要使用此数据更新视图模型(不使用映射插件),您应该写:

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

你必须为你想在页面上显示的每个变量这样做。 如果您的数据结构变得更加复杂(例如,它们包含子节点或包含数组),则手动处理变得非常麻烦。 映射插件允许您做的是创建从常规JavaScript对象(或JSON结构)到可观察视图模型的映射。

 

示例:使用ko.mapping

要通过映射插件创建视图模型,请将上面代码中的viewModel的创建替换为ko.mapping.fromJS函数:

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

如何映射

  • 对象的所有属性都将转换为observable。 如果更新将更改值,它将更新observable。
  • 数组被转换为可观察数组。 如果更新会更改项目数,则它将执行适当的添加/删除操作。 它还将尝试保持与原始JavaScript数组相同的顺序。

取消映射

如果要将映射对象转换回常规JS对象,请使用:

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

这将创建一个未映射的对象,只包含作为原始JS对象的一部分的映射对象的属性。 因此,换句话说,您手动添加到视图模型的任何属性或函数都将被忽略。 默认情况下,此规则的唯一例外是_destroy属性,它也将被映射回来,因为它是Knockout在从ko.observableArray中销毁项目时可能生成的属性。 有关如何配置此项的更多详细信息,请参阅“高级应用”章节。

使用JSON字符串

如果您的Ajax调用返回一个JSON字符串(并且不将其反序列化为JavaScript对象),那么您可以使用函数ko.mapping.fromJSON来创建和更新视图模型。 要取消映射,可以使用ko.mapping.toJSON。

除了它们使用JSON字符串而不是JS对象的事实,这些函数与它们的JS对象完全相同。

高级用法

有时,可能有必要对如何执行映射有更多的控制。 这是使用映射选项实现的。 它们可以在ko.mapping.fromJS调用期间指定。 在后续调用中,您不需要再次指定它们。

在这种情况下,您可能需要使用这些映射选项。

使用“key”唯一标识对象

假设您有一个类似于以下内容的JavaScript对象:

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

您可以将此映射到视图模型没有任何问题:

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

现在,假设数据更新为没有任何拼写错误:

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

这里发生了两件事情:name从Scot改为Scott,children[0] .name从Alicw变成了Alice。 您可以基于此新数据更新viewModel:

ko.mapping.fromJS(data, viewModel);

和名称会改变,如预期。 但是,在children数组中,子(Alicw)将被完全删除,并添加一个新的(Alice)。 这不是完全你会期望的。 相反,你会希望只有孩子的name属性从Alicw更新到Alice,而不是整个孩子被替换!

这是因为,默认情况下,映射插件只是比较数组中的两个对象。 因为在JavaScript中,对象{id:1,name:'Alicw'}不等于{id:1,name:'Alice'},它认为整个孩子需要被删除并被一个新的替换。

要解决这个问题,你可以指定映射插件应该使用哪个键来确定对象是新的还是旧的。 你可以这样设置:

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

这样,每次映射插件检查children数组中的一个项目时,它只会查看id属性来确定一个对象是否被完全替换或只是需要更新。

使用“create”定制对象构造

如果你想自己处理映射的一部分,你也可以提供一个create回调。 如果这个回调存在,映射插件将允许你自己做这部分的映射。

假设您有一个类似于以下内容的JavaScript对象:

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

如果你想自己映射children数组,你可以这样指定:

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

提供给create回调的options参数是一个JavaScript对象,其中包含:

  • data: 包含此子项的数据的JavaScript对象
  • parent: 此子项所属的父对象或数组

当然,在创建回调中你可以做另一个调用ko.mapping.fromJS如果你愿意。 一个典型的用例可能是,如果你想用一些额外的计算observables扩充原始的JavaScript对象:

var myChildModel = function(data) {
    ko.mapping.fromJS(data, {}, this);
     
    this.nameLength = ko.computed(function() {
        return this.name().length;
    }, this);
}

使用“update”定制对象更新

您还可以通过指定更新回调来自定义对象的更新方式。 它将接收它试图更新的对象和一个与create回调所使用的相同的选项对象。 您应该返回更新的值。

提供给更新回调的options参数是一个JavaScript对象,包含:* data:包含此子节点数据的JavaScript对象* parent:此子节点所属的父对象或数组* 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());

这将弹窗提示Graham Foo!

使用“ignore”忽略某些属性

如果你想映射插件忽略你的JS对象的一些属性(即不映射它们),你可以指定一个属性名称数组来忽略:

var mapping = {
    'ignore': ["propertyToIgnore", "alsoIgnoreThis"]
}
var viewModel = ko.mapping.fromJS(data, mapping);

您在映射选项中指定的忽略数组与默认忽略数组相结合。 你可以像这样操作这个默认数组:

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

使用“include”包括某些属性

当将视图模型转换回JS对象时,默认情况下,映射插件将仅包含属于原始视图模型的属性,但它也将包括Knockout生成的_destroy属性,即使它不是原始对象的一部分 。 但是,您可以选择自定义此数组:

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"];

使用“copy”复制某些属性

将视图模型转换回JS对象时,默认情况下,映射插件将根据上述规则创建可观察项。 如果你想强制映射插件简单地复制属性而不是使其可见,添加其名称到“复制”数组:

var mapping = {
    'copy': ["propertyToCopy"]
}
var viewModel = ko.mapping.fromJS(data, mapping);

您在映射选项中指定的副本数组与默认副本数组相结合,默认情况下为空。 你可以像这样操作这个默认数组:

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

仅使用“observe”观察某些属性

如果你希望映射插件只创建你的JS对象的一些属性的可观察和复制,你可以指定一个属性名称数组观察:

var mapping = {
    'observe': ["propertyToObserve"]
}
var viewModel = ko.mapping.fromJS(data, mapping);

您在映射选项中指定的观察数组与默认的观察数组相结合,默认情况下为空。 你可以像这样操作这个默认数组:

var oldOptions = ko.mapping.defaultOptions().observe;
ko.mapping.defaultOptions().observe = ["onlyObserveThis"];

数组忽略并包括仍然正常工作。 数组副本可用于复制数组或对象属性(包括子元素)的效率。 如果在copy或observe中没有指定数组或对象属性,那么它将被递归映射:

var data = {
    a: "a",
    b: [{ b1: "v1" }, { b2: "v2" }] 
};
 
var result = ko.mapping.fromJS(data, { observe: "a" });
var result2 = ko.mapping.fromJS(data, { observe: "a", copy: "b" }); //will be faster to map.

结果和结果2将是:

{
    a: observable("a"),
    b: [{ b1: "v1" }, { b2: "v2" }] 
}

复制和观察可以冲突:

var data = {
    a: "a",
    b: [{ b1: "v1" }, { b2: "v2" }] 
};
var result = ko.mapping.fromJS(data, { observe: "b[0].b1"});
var result2 = ko.mapping.fromJS(data, { observe: "b[0].b1", copy: "b" });

结果将是:

{
    a: "a",
    b: [{ b1: observable("v1") }, { b2: "v2" }] 
}

结果2将是:

{
    a: "a",
    b: [{ b1: "v1" }, { b2: "v2" }] 
}

指定更新目标

如果,像在上面的例子中,你正在一个类中执行映射,你希望有它作为你的映射操作的目标。 ko.mapping.fromJS的第三个参数指示目标。 例如,

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

所以,如果你想映射一个JavaScript对象到这里,你可以传递这个作为第三个参数:

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

从多个来源映射

您可以通过应用多个ko.mapping.fromJS调用在一个视图模型中组合多个JS对象,例如:

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

在每个调用中指定的映射选项将被合并。

映射监控属性数组

由映射插件生成的可观察数组通过一些可以利用键映射的函数来扩充:

  • mappedRemove
  • mappedRemoveAll
  • mappedDestroy
  • mappedDestroyAll
  • mappedIndexOf

它们在功能上等同于常规的ko.observableArray函数,但是可以基于对象的键来做事情。 例如:

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

映射的observablearray还公开了一个映射的Create函数:

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

它将首先检查键是否已经存在,如果是,将抛出异常。 接下来,它将调用create和update回调(如果有的话)来创建新对象。 最后,它将把这个对象添加到数组并返回它。

KnockoutJS 3.X 结语

至此,所有KnockoutJS 3.X的API文档撰写完毕,希望这个完整的KnockoutJS中文文档能对你有所帮助,感谢你的阅读。如果你觉得不错,请点一波推荐,关注。如果你觉得文中有那些不妥,欢迎批评指正。

感谢您的阅读。

转载请注明出处:http://www.cnblogs.com/smallprogram/ 

再次感谢

posted @ 2016-10-19 13:08  SmallProgram  阅读(3322)  评论(1编辑  收藏  举报