Javascript Knockout.js 使每个嵌套对象都成为 Observable
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/10555115/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Knockout.js Make every nested object an Observable
提问by frapontillo
I am using Knockout.js as a MVVM library to bind my data to some pages. I'm currently building a library to make REST calls to a web service. My RESTful web service returns a simple structure:
我使用 Knockout.js 作为 MVVM 库将我的数据绑定到某些页面。我目前正在构建一个库来对 Web 服务进行 REST 调用。我的 RESTful Web 服务返回一个简单的结构:
{
id : 1,
details: {
name: "Johnny",
surname: "Boy"
}
}
I have an observable main parent, myObject
.
When I do
我有一个可观察的主要父母,myObject
. 当我做
myObject(ko.mapping.fromJS(data))
the observables in myObject
are:
中的观察值myObject
是:
id
name
surname
id
name
surname
How can I make details
(and theoretically any object in the structure an observable)? I need this behavior so that i can set a computed observable on details and get noticed as soon as any of the internal data changes.
我怎样才能details
(理论上结构中的任何对象都是可观察的)?我需要这种行为,以便我可以在细节上设置一个计算的可观察对象,并在任何内部数据发生变化时立即被注意到。
I have set up a basic recursive function which should do the trick. It doesn't, of course, myObject.details
doesn't become an observable.
我已经建立了一个基本的递归函数,它应该可以解决问题。当然,myObject.details
它不会成为可观察的。
// Makes every object in the tree an observable.
var makeAllObservables = function () {
makeChildrenObservables(myObject);
};
var makeChildrenObservables = function (object) {
// Make the parent an observable if it's not already
if (!ko.isObservable(object)) {
if ($.isArray(object))
object = ko.observableArray(object);
else
object = ko.observable(object);
}
// Loop through its children
for (var child in object()) {
makeChildrenObservables(object()[child]);
}
};
I'm pretty sure it's something about incorrect references, but how can I solve this? Thank you.
我很确定这是关于不正确的引用,但我该如何解决这个问题?谢谢你。
采纳答案by Jason Goemaat
I don't think knockout has a built-in way to observe changes to child elements. If I understand your question, when someone changes the name you want a change to details as an entity to be noticed. Can you give a concrete example of how you would use this? Would you use a subscription to the details observable to perform some action?
我不认为淘汰赛有一种内置的方式来观察子元素的变化。如果我理解您的问题,当有人更改名称时,您希望更改作为一个实体的详细信息。你能举一个具体的例子来说明你将如何使用它吗?你会使用对可观察细节的订阅来执行某些操作吗?
The reason your code doesn't make details an observable is because javascript is pass by value, so changing the value of the 'object' argument in your function doesn't change the actual value you passed, only the value of the argument inside your function.
您的代码没有使细节成为可观察的原因是因为 javascript 是按值传递的,因此更改函数中“对象”参数的值不会更改您传递的实际值,只会更改您内部的参数值功能。
Edit
编辑
If changes willautomatically propagate to the parents, this should make all children observable I think, but your root that you pass the first time should already be an observable.
如果更改会自动传播到父母,我认为这应该使所有孩子都可以观察到,但是您第一次通过的根应该已经是可观察的。
// object should already be observable
var makeChildrenObservables = function (object) {
if(!ko.isObservable(object)) return;
// Loop through its children
for (var child in object()) {
if (!ko.isObservable(object()[child])) {
object()[child] = ko.observable(object()[child]);
}
makeChildrenObservables(object()[child]);
}
};
回答by Paolo del Mundo
I would use the knockout mapping plugin.
我会使用淘汰赛映射插件。
var jsonData = {
id : 1,
details: {
name: "Johnny",
surname: "Boy"
}
}
var yourMapping = {
'details': {
create: function(options) {
return Details(options.data);
}
}
}
function Details(data) {
ko.mapping.fromJS(data, {}, this);
}
function YourObjectName() {
ko.mapping.fromJS(jsonData, yourMapping, this);
}
This will create your object hierarchy with all of the children as observables.
这将创建您的对象层次结构,并将所有子项作为可观察对象。
回答by Beans
From what I have experienced, ko.mapping.fromJS does not make an observable out of an object.
根据我的经验, ko.mapping.fromJS 不会从对象中生成可观察对象。
Let's say you have this ViewModel constructor:
假设您有这个 ViewModel 构造函数:
var VM = function(payload) {
ko.mapping.fromJS(payload, {}, this);
}
and this data object:
和这个数据对象:
var data1 = {
name: 'Bob',
class: {
name: 'CompSci 101',
room: 112
}
}
}
and you use data1 to create VM1:
然后使用 data1 创建 VM1:
var VM1 = new VM(data1);
Then VM1.class will not be a ko.observable, it is a plain javascript Object.
那么 VM1.class 将不是 ko.observable,它是一个普通的 javascript 对象。
If you then create another viewmodel using a data object with a null class member, ie:
如果您然后使用具有空类成员的数据对象创建另一个视图模型,即:
var data2 = {
name: 'Bob',
class: null
}
var VM2 = new VM(data2);
then VM2.class is a ko.observable.
那么 VM2.class 是一个 ko.observable。
If you then execute:
如果然后执行:
ko.mapping(data1, {}, VM2)
then VM2.class remains a ko.observable.
那么 VM2.class 仍然是 ko.observable。
So, if you create a ViewModel from a seed data object where object members are null, and then popuplate them with a populated data object, you will have observable class members.
因此,如果您从对象成员为空的种子数据对象创建 ViewModel,然后使用填充的数据对象填充它们,您将拥有可观察的类成员。
This leads to problems, because sometimes the object members are observables, and sometimes they are not. Form bindings will work with VM1 and not work with VM2. It would be nice if ko.mapping.fromJS always made everything a ko.observable so it was consistent?
这会导致问题,因为有时对象成员是可观察的,有时不是。表单绑定适用于 VM1,不适用于 VM2。如果 ko.mapping.fromJS 总是将所有内容都设为 ko.observable 以便保持一致,那该多好?
回答by AR M
By using Knockout-Pluginwe can make child elements observable .We have lot of options to manage how we want our data to make observable.
通过使用Knockout-Plugin,我们可以使子元素变得可观察。我们有很多选项来管理我们希望我们的数据如何变得可观察。
Here is a sample code :
这是一个示例代码:
var data = {
people: [
{
id: 1,
age: 25,
child : [
{id : 1,childname : "Alice"},
{id : 2,childname : "Wonderland"}
]
},
{id: 2, age: 35}
],
Address:[
{
AddressID : 1,
City : "NewYork",
cities : [
{
cityId : 1,
cityName : "NewYork"
},
{
cityId :2,
cityName : "California"
}
]
},
{
AddressID : 2,
City : "California",
cities : [
{
cityId :1,
cityName : "NewYork"
},
{
cityId :2,
cityName : "California"
}
]
}
],
isSelected : true,
dataID : 6
};
var mappingOptions = {
people: {
create: function(options) {
console.log(options);
return ko.mapping.fromJS(options.data, childmappingOptions);
}
},
Address: {
create: function(options) {
console.log(options);
return ko.mapping.fromJS(options.data, childmappingOptions);
}
}
};
var childmappingOptions = {
child: {
create: function(options) {
return ko.mapping.fromJS(options.data, { observe: ["id","childname"]});
}
},
cities :{
create: function(options) {
return ko.mapping.fromJS(options.data, { observe: ["cityId","cityName"]});
}
}
};
var r = ko.mapping.fromJS(data, mappingOptions);
I have attached a working fiddle: http://jsfiddle.net/wmqTx/5/
我附上了一个工作小提琴:http: //jsfiddle.net/wmqTx/5/
回答by AlexRebula
I will extend Paolo del Mundo's answer (which I think it might easily be the best and the only solution at this moment) with my example solution.
我将用我的示例解决方案扩展Paolo del Mundo的答案(我认为这很容易成为目前最好和唯一的解决方案)。
Consider frapontillo's original object:
考虑frapontillo的原始对象:
{
id : 1,
details: {
name: "Johnny",
surname: "Boy"
}
}
The details
property itself is an object and as such CAN'T be an observable. The same goes for the User
property in the example below, which is also an object. Those two objects cannot be observables but their LEAF properties can be.
该details
属性本身是一个对象,因此无法可观察到的。User
下面示例中的属性也是如此,它也是一个对象。这两个对象不能是可观察的,但它们的 LEAF 属性可以是.
Every leaf property of your data tree / model CAN BE AN OBSERVABLE.The easiest way to achieve that is that you properly define the mapping model before passing it to the mapping pluginas parameter.
数据树/模型的每个叶子属性都可以观察到。实现这一点的最简单方法是在将映射模型作为参数传递给映射插件之前正确定义映射模型。
See my example below.
请参阅下面的示例。
EXAMPLE:
例子:
Let's say we need to show an html page / view where we have a list of users on a grid. Beside the Users grid a form for editing a selected user from the grid is shown.
假设我们需要显示一个 html 页面/视图,其中我们在网格上有一个用户列表。在用户网格旁边显示了一个用于编辑网格中选定用户的表单。
STEP 1: DEFINING THE MODELS
第 1 步:定义模型
function UsersEdit() {
this.User = new User(); // model for the selected user
this.ShowUsersGrid = ko.observable(false); // defines the grid's visibility (false by default)
this.ShowEditForm = ko.observable(false); // defines the selected user form's visibility (false by default)
this.AllGroups = []; // NOT AN OBSERVABLE - when editing a user in the user's form beside the grid a multiselect of all available GROUPS is shown (to place the user in one or multiple groups)
this.AllRoles = []; // NOT AN OBSERVABLE - when editing a user in the user's form beside the grid a multiselect of all available ROLES is shown (to assign the user one or multiple roles)
}
function User() {
this.Id = ko.observable();
this.Name = ko.observable();
this.Surname = ko.observable();
this.Username = ko.observable();
this.GroupIds = ko.observableArray(); // the ids of the GROUPS that this user belongs to
this.RoleIds = ko.observableArray(); // the ids of the ROLES that this user has
}
STEP 2: MAPPING (TO GET NESTED OBSERVABLES)
第 2 步:映射(获取嵌套的观察值)
Let's say this is your raw JSON model with data that you want to map and get a KO model with nested observables.
假设这是您的原始 JSON 模型,其中包含您想要映射的数据并获得带有嵌套 observable 的 KO 模型。
var model = {
User: {
Id: 1,
Name: "Johnny",
Surname = "Boy",
Username = "JohhnyBoy",
GroupIds = [1, 3, 4],
RoleIds = [1, 2, 5]
}
};
Now that all of this is defined, you can map:
现在所有这些都已定义,您可以映射:
var observableUserEditModel = ko.mapping.fromJS(model, new UsersEdit());
AND YOU'RE DONE! :)
大功告成!:)
The observableUserEditModel will hold all of your observables, even nested ones. Now the only thing you need to take care of in order to test this is to bind the observableUserEditModel
object with your HTML. Hint: use the with
binding and test the observable observableUserEditModel
data structure inserting this in your HTML view:
observableUserEditModel 将保存所有的 observable,甚至是嵌套的。现在,为了测试这一点,您唯一需要注意的是将observableUserEditModel
对象与您的 HTML绑定。提示:使用with
绑定并测试将其observableUserEditModel
插入 HTML 视图中的可观察数据结构:
<pre data-bind="text: ko.toJSON($data, null, 2)"></pre>
回答by Beans
Maybe in a future version there could be a configuration option that causes ko.mapping.fromJS to always create observables. It could be enabled for new projects or after updating bindings of an existing project.
也许在未来的版本中可能会有一个配置选项导致 ko.mapping.fromJS 总是创建可观察对象。可以为新项目或在更新现有项目的绑定后启用它。
What I do to prevent this problem is ensure that model seeds always have Objects member properties populated, at every level. In this way, all object properties are mapped as POJOs (Plain Old Javascript Objects), so the ViewModel doesn't initialize them as ko.observables. It avoids the "sometimes observables, sometimes not" issue.
我为防止出现此问题所做的工作是确保模型种子始终在每个级别都填充 Objects 成员属性。通过这种方式,所有对象属性都映射为 POJO(Plain Old Javascript Objects),因此 ViewModel 不会将它们初始化为 ko.observables。它避免了“有时是可观察的,有时不是”问题。
Best regards, Mike
最好的问候,迈克