(九)Knockout 进一步技术
加载和保存 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
})
… 或者可以将数据发送到服务器r:
var data = /* Your data in JSON format - see below */;
$.post("/some/url", data, function(returnedData) {
// This callback is executed if the post was successful
})
将视图模型数据转换为纯JSON
视图模型是JavaScript对象,因此在某种意义上,您可以使用任何标准的JSON序列化器(比如JSON)将它们序列化为JSON.stringify(现代浏览器中的一个本机函数)或json2.js
库。然而,您的视图模型可能包含可观察性、计算可观察性和可观察数组,这些数组被实现为JavaScript函数,因此如果不进行额外的工作,就不能始终干净地序列化。
为了便于序列化视图模型数据,包括可观察对象等,Knockout包括两个帮助函数:
ko.toJS
— 这个克隆您的view mode的对象图,替换每个可观察对象的当前值,这样您就得到了一个纯拷贝,它只包含您的数据,没有与Knockout相关的工件。ko.toJSON
— 这将生成一个JSON字符串,表示view model 的数据。在内部,它只是在view model上调用ko.toJS
然后在结果上使用浏览器的原生JSON序列化器。注意:对于没有原生JSON序列化器的旧浏览器(如ie7或更早版本),要使其工作,还必须引用json2.js
库。
例如,定义一个视图模型,如下所示:
var viewModel = {
firstName : ko.observable("Bert"),
lastName : ko.observable("Smith"),
pets : ko.observableArray(["Cat", "Dog", "Fish"]),
type : "Customer"
};
viewModel.hasALotOfPets = ko.computed(function() {
return this.pets().length > 2
}, viewModel)
这包含可观测值、计算的可观测值、可观测数组和普通值的混合。您可以使用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: plain js现在是一个纯JavaScript对象,其中没有任何可观察的内容。这只是数据。
// The object is equivalent to the following:
// {
// firstName: "Bert",
// lastName: "Smith",
// pets: ["Cat","Dog","Fish"],
// type: "Customer",
// hasALotOfPets: true
// }
请注意,ko.toJSON
接受与 JSON.stringify相同的参数。例如,在调试Knockout应用程序时,拥有视图模型数据的“实时”表示可能很有用。要为此生成格式良好的显示,您可以将spaces参数传递到ko.toJSON
中,并绑定到您的视图模型,如:
<pre data-bind="text: ko.toJSON($root, null, 2)"></pre>
使用JSON更新视图模型数据
如果您已经从服务器加载了一些数据,并且想要使用它来更新视图模型,那么最直接的方法就是自己动手。例如,
// Load and parse the JSON
var someJSON = /* 忽略: 从服务器上以您想要的方式获取它 */;
var parsed = JSON.parse(someJSON);
// Update view model properties
viewModel.firstName(parsed.firstName);
viewModel.pets(parsed.pets);
在许多场景中,这种直接方法是最简单和最灵活的解决方案。当然,当您更新视图模型上的属性时,Knockout将负责更新可视UI以匹配它。
然而,许多开发人员更喜欢使用基于约定的方法来使用传入数据更新视图模型,而不需要为每个要更新的属性手动编写一行代码。如果视图模型具有许多属性或深度嵌套的数据结构,这将是有益的,因为它可以大大减少您需要编写的手工映射代码的数量。有关这项技术的更多细节,请参见 the knockout.mapping plugin插件。
使用扩展器来增强 observables
Knockout observables提供支持读/写值所需的基本功能,并在该值发生变化时通知订阅者。 但是,在某些情况下,您可能希望向可观察对象添加其他功能。 这可能包括向可观察对象添加附加属性,或者通过在可观察对象前面放置可写的计算可观察对象来拦截写入。 Knockout扩展器提供了一种简单灵活的方法来对可观察的这种类型的扩充。
如何创建扩展程序
创建扩展器需要向 ko.extenders
添加一个函数来延伸部分对象。该函数将可观察对象本身作为第一个参数,将任何选项作为第二个参数。然后它可以返回可观察的,或者返回一些新的东西,比如一个计算的可观察的,以某种方式使用原始的可观察的。
这个简单的logChange
extender订阅可观察对象,并使用控制台编写任何更改以及可配置的消息。
ko.extenders.logChange = function(target, option) {
target.subscribe(function(newValue) {
console.log(option + ": " + newValue);
});
return target;
};
您可以通过调用一个可观察对象的extend
函数并传递一个包含logChange
属性的对象来使用这个扩展程序。
this.firstName = ko.observable("Bob").extend({logChange: "first name"});
如果 firstName
observable的值被更改为Ted
,那么控制台将显示firstName: Ted
。
Live Example 1: 强制输入是数字
本例创建了一个扩展器,该扩展器强制将写入到可观察对象的数据四舍五入到可配置的精度级别。在这种情况下,扩展器将返回一个新的可写计算可观察对象,该可写计算可观察对象将位于实际可观察到的拦截写之前。
Source code: View
<p><input data-bind="value: myNumberOne" /> (round to whole number)</p>
<p><input data-bind="value: myNumberTwo" /> (round to two decimals)</p>
Source code: View model
ko.extenders.numeric = function(target, precision) {
//create a writable computed observable to intercept writes to our observable
var result = ko.pureComputed({
read: target, //always return the original observables value
write: function(newValue) {
var current = target(),
roundingMultiplier = Math.pow(10, precision),
newValueAsNum = isNaN(newValue) ? 0 : +newValue,
valueToWrite = Math.round(newValueAsNum * roundingMultiplier) / roundingMultiplier;
//only write if it changed
if (valueToWrite !== current) {
target(valueToWrite);
} else {
//if the rounded value is the same, but a different value was written, force a notification for the current field
if (newValue !== current) {
target.notifySubscribers(valueToWrite);
}
}
}
}).extend({ notify: 'always' });
//initialize with current value to make sure it is rounded appropriately
result(target());
//return the new computed observable
return result;
};
function AppViewModel(one, two) {
this.myNumberOne = ko.observable(one).extend({ numeric: 0 });
this.myNumberTwo = ko.observable(two).extend({ numeric: 2 });
}
ko.applyBindings(new AppViewModel(221.2234, 123.4525));
注意,为了自动从UI中删除被拒绝的值,必须在计算的观察对象上使用.extend({notify: ‘always‘})
。如果没有这个,用户可能会输入一个无效的newValue
,当四舍五入时,它会给出一个未更改的valueToWrite
。然后,由于模型值不会更改,所以不会有更新UI中的文本框的通知。使用{notify: ‘always‘}
会导致文本框刷新(删除被拒绝的值),即使计算的属性没有更改值。
Live Example 2: 向可观察对象添加验证
这个例子创建了一个扩展器,它允许根据需要对可观察对象进行标记。该扩展器不返回新对象,而只是向现有的可观察对象添加额外的子可观察对象。由于可观察对象是函数,它们实际上可以有自己的属性。然而,当视图模型被转换成JSON时,子可观察对象将被删除,只剩下实际可观察对象的值。这是一种添加仅与UI相关且不需要发送回服务器的附加功能的好方法。
Source code: View
<p data-bind="css: { error: firstName.hasError }">
<input data-bind='value: firstName, valueUpdate: "afterkeydown"' />
<span data-bind='visible: firstName.hasError, text: firstName.validationMessage'> </span>
</p>
<p data-bind="css: { error: lastName.hasError }">
<input data-bind='value: lastName, valueUpdate: "afterkeydown"' />
<span data-bind='visible: lastName.hasError, text: lastName.validationMessage'> </span>
</p>
Source code: View model
ko.extenders.required = function(target, overrideMessage) {
//add some sub-observables to our observable
target.hasError = ko.observable();
target.validationMessage = ko.observable();
//define a function to do validation
function validate(newValue) {
target.hasError(newValue ? false : true);
target.validationMessage(newValue ? "" : overrideMessage || "This field is required");
}
//initial validation
validate(target());
//validate whenever the value changes
target.subscribe(validate);
//return the original observable
return target;
};
function AppViewModel(first, last) {
this.firstName = ko.observable(first).extend({ required: "Please enter a first name" });
this.lastName = ko.observable(last).extend({ required: "" });
}
ko.applyBindings(new AppViewModel("Bob","Smith"));
应用多个扩展程序
在对可观察对象的.extend
方法的一次调用中可以应用多个扩展程序。
this.firstName = ko.observable(first).extend({ required: "Please enter a first name", logChange: "first name" });
在这种情况下,required
和logChange
扩展器都将针对我们的observable执行。
原文:https://www.cnblogs.com/tangge/p/10856423.html