Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/javascript/422.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Javascript js使每个嵌套对象都成为可观察对象_Javascript_Mvvm_Knockout.js_Knockout Mapping Plugin_Knockout 2.0 - Fatal编程技术网

Javascript js使每个嵌套对象都成为可观察对象

Javascript js使每个嵌套对象都成为可观察对象,javascript,mvvm,knockout.js,knockout-mapping-plugin,knockout-2.0,Javascript,Mvvm,Knockout.js,Knockout Mapping Plugin,Knockout 2.0,我使用Knockout.js作为MVVM库将数据绑定到某些页面。我目前正在构建一个库,以便对web服务进行REST调用。 我的RESTful web服务返回一个简单的结构: { id : 1, details: { name: "Johnny", surname: "Boy" } } 我有一个可观察的主父对象,myObject。 当我这样做的时候 myObject(ko.mapping.fromJS(data)) myObject中的可

我使用Knockout.js作为MVVM库将数据绑定到某些页面。我目前正在构建一个库,以便对web服务进行REST调用。 我的RESTful web服务返回一个简单的结构:

{
    id : 1,
    details: {
        name: "Johnny",
        surname: "Boy"
    }
}
我有一个可观察的主父对象,
myObject
。 当我这样做的时候

myObject(ko.mapping.fromJS(data))
myObject
中的可观测值为:

  • id
  • name
  • 姓氏
我怎样才能使
细节
(理论上结构中的任何对象都是可观察的)?我需要这种行为,这样我就可以在细节上设置一个可观察的计算值,并在任何内部数据发生变化时立即得到注意

我已经建立了一个基本的递归函数,它应该可以做到这一点。当然,
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]);
    }
};
// 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]);
    }
};
我很确定这是关于不正确的引用,但是我怎么才能解决这个问题呢?谢谢。

我会用这个


这将创建对象层次结构,其中所有子元素都是可观察的。

我认为knockout没有内置的方法来观察子元素的更改。如果我理解你的问题,当有人更改名称时,你希望作为一个实体对细节进行更改以引起注意。你能举一个具体的例子说明你将如何使用这个吗?您是否会使用对可观察到的细节的订阅来执行某些操作

var model = {
    User: {
        Id: 1,
        Name: "Johnny",            
        Surname = "Boy",
        Username = "JohhnyBoy",
        GroupIds = [1, 3, 4],
        RoleIds = [1, 2, 5]
    }
};
代码没有将细节设置为可观察的原因是javascript是按值传递的,因此更改函数中“object”参数的值不会更改传递的实际值,只更改函数中参数的值

编辑

如果更改将自动传播到父级,我认为这将使所有子级都可见,但是您第一次通过的根应该已经可见

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

根据我的经验,ko.mapping.fromJS不会使对象成为可观察的对象

假设您有这个ViewModel构造函数:

var VM = function(payload) {
  ko.mapping.fromJS(payload, {}, this);
}
这个数据对象:

var data1 = {
  name: 'Bob',
  class: {
    name: 'CompSci 101',
    room: 112
  }
}

您可以使用data1创建VM1:

var VM1 = new VM(data1);
那么VM1.class将不是一个ko.observable,它是一个普通的javascript对象

如果随后使用具有空类成员的数据对象创建另一个viewmodel,即:

var data2 = {
  name: 'Bob',
  class: null
}
var VM2 = new VM(data2);
那么VM2.class是一个ko.observable

如果您随后执行:

ko.mapping(data1, {}, VM2)
那么VM2.class仍然是一个可观察的对象

因此,如果您从对象成员为null的种子数据对象创建ViewModel,然后使用填充的数据对象对其进行popuplate,那么您将拥有可观察的类成员


这会导致问题,因为有时对象成员是可观察的,有时则不是。表单绑定将适用于VM1,而不适用于VM2。如果ko.mapping.fromJS总是将所有内容都设置为ko.observable,那么这将是一件好事,因为它是一致的?

也许在未来的版本中,可能会有一个配置选项,使ko.mapping.fromJS总是创建observable。它可以在新项目中启用,也可以在更新现有项目的绑定后启用

我要做的是确保模型种子始终在每个级别填充对象成员属性,以防止出现此问题。通过这种方式,所有对象属性都映射为POJO(普通的旧Javascript对象),因此ViewModel不会将它们初始化为ko.observates。它避免了“有时可见,有时不可见”的问题

致以最良好的祝愿, Mike

通过使用,我们可以使子元素可见。我们有很多选项来管理我们希望数据如何可见

// 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]);
    }
};
// 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]);
    }
};
下面是一个示例代码:

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

我附上了一个有用的提琴:

我将用我的示例解决方案扩展保罗·德尔蒙多的答案(我认为这可能是目前最好的也是唯一的解决方案)

考虑弗拉蓬蒂略的原始对象:

{
    id : 1,
    details: {
        name: "Johnny",
        surname: "Boy"
    }
}
details
属性本身是一个对象,因此不能被观察到。下面示例中的
User
属性也是如此,它也是一个对象。这两个对象不能被观察到,但它们的叶子属性可以被观察到

数据树/模型的每个叶属性都可以是可观察的。实现这一点的最简单方法是在将映射模型作为参数传递给映射插件之前正确定义映射模型

请参见下面的示例

示例:

假设我们需要显示一个html页面/视图,其中有一个网格上的用户列表。在用户网格旁边,将显示用于编辑网格中选定用户的表单

步骤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
}
第2步:映射(获取嵌套的观测值)

假设这是您的原始JSON模型,其中包含您想要映射的数据,并获得一个具有嵌套观测值的KO模型

var model = {
    User: {
        Id: 1,
        Name: "Johnny",            
        Surname = "Boy",
        Username = "JohhnyBoy",
        GroupIds = [1, 3, 4],
        RoleIds = [1, 2, 5]
    }
};
现在,所有这些都已定义,您可以映射:

var observableUserEditModel = ko.mapping.fromJS(model, new UsersEdit());
你就完了!:)

observableUserEditModel将保存所有的可观察对象,甚至是嵌套的。现在,为了测试这一点,您需要注意的唯一一件事是将
observeUserEditModel
对象与HTML绑定。提示:将
绑定一起使用,并测试可观察的
observableUserEditModel
数据结构将其插入HTML视图:

<pre data-bind="text: ko.toJSON($data, null, 2)"></pre>


是的,我已经考虑过这个解决方案,问题是我需要对许多比本例中的on大得多的结构执行此操作,我正在寻找一种自动执行此操作的方法。很遗憾,这是我所知道的唯一也是最好的执行此操作的方法。这意味着您需要在编码之前对数据进行建模。我同意,这可能是一项繁琐的工作,因为这意味着您需要为要映射/具有可观测对象的每种类型的对象预定义一个模型。但是想想。。。必须有一种方法来说明如何映射属性。不是麦格