35

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

{
    id : 1,
    details: {
        name: "Johnny",
        surname: "Boy"
    }
}

我有一个可观察的主要父母,myObject. 当我做

myObject(ko.mapping.fromJS(data))

中的可观察值myObject是:

  • id
  • name
  • surname

我怎样才能使details(理论上结构中的任何对象都是可观察的)?我需要这种行为,以便我可以在细节上设置一个计算出的 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]);
    }
};

我很确定这是关于不正确的引用,但我该如何解决这个问题?谢谢你。

4

6 回答 6

23

我会使用淘汰赛映射插件

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

这将创建您的对象层次结构,其中所有子对象都作为可观察对象。

于 2012-05-11T18:54:37.393 回答
14

我认为淘汰赛没有内置的方法来观察子元素的变化。如果我理解您的问题,当有人更改名称时,您希望将详细信息更改为要注意的实体。你能举一个具体的例子来说明你将如何使用它吗?你会使用订阅可观察到的细节来执行一些操作吗?

您的代码没有使细节成为可观察的原因是因为 javascript 是按值传递的,因此更改函数中“对象”参数的值不会更改您传递的实际值,只会更改您内部参数的值功能。

编辑

如果更改自动传播给父母,我认为这应该使所有孩子都可以观察到,但是您第一次通过的根应该已经是可观察的。

// 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]);
    }
};
于 2012-05-12T06:47:47.220 回答
3

通过使用Knockout-Plugin,我们可以使子元素可观察。我们有很多选项来管理我们希望我们的数据如何变得可观察。

这是一个示例代码:

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

我附上了一个工作小提琴:http: //jsfiddle.net/wmqTx/5/

于 2014-07-10T07:30:54.247 回答
3

根据我的经验,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 对象。

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

var data2 = {
  name: 'Bob',
  class: null
}
var VM2 = new VM(data2);

那么 VM2.class 是一个 ko.observable。

如果你然后执行:

ko.mapping(data1, {}, VM2)

那么 VM2.class 仍然是一个 ko.observable。

因此,如果您从对象成员为空的种子数据对象创建 ViewModel,然后使用填充的数据对象填充它们,您将拥有可观察的类成员。

这会导致问题,因为有时对象成员是可观察的,有时则不是。表单绑定将适用于 VM1,但不适用于 VM2。如果 ko.mapping.fromJS 总是让所有东西都变成 ko.observable 这样它是一致的,那就太好了?

于 2014-02-27T03:23:40.867 回答
2

我将用我的示例解决方案扩展Paolo del Mundo的答案(我认为它可能很容易成为目前最好和唯一的解决方案)。

考虑frapontillo的原始对象:

{
    id : 1,
    details: {
        name: "Johnny",
        surname: "Boy"
    }
}

details属性本身是一个对象,因此不能是可观察的。下面示例中的User属性也是如此,它也是一个对象。这两个对象不能是 observables ,但它们的 LEAF 属性可以是.

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

请参阅下面的示例。

例子:

假设我们需要显示一个 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 步:映射(获取嵌套的 OBSERVABLES)

假设这是您的原始 JSON 模型,其中包含您想要映射的数据并获得具有嵌套 observables 的 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 将保存所有的 observable,甚至是嵌套的。现在,为了测试这一点,您唯一需要注意的是将observableUserEditModel对象与您的 HTML 绑定。提示:使用绑定并测试在您的 HTML 视图中插入with的可观察数据结构:observableUserEditModel

<pre data-bind="text: ko.toJSON($data, null, 2)"></pre>
于 2015-04-04T13:59:42.297 回答
1

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

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

最好的问候,迈克

于 2014-02-28T03:32:42.073 回答