Knockout映射插件:使分层对象结构中的每个节点都可观察

时间:2014-07-28 12:05:40

标签: knockout.js mapping observable hierarchical

我使用Knockout和ko.mapping插件来创建和更新我的viewmodel。我有一个像服务器这样的json结构,例如:

var viewmodel = {
    name: "Foo",
    person: {
        forename: "John",
        surname: "Smith"
    }
};

当我使用ko.mapping.fromJS(viewmodel)创建我的viewmodel时,只有叶节点被转换为observables,所以我得到类似于以下内容的东西:

var viewmodel = {
    name: ko.observable("Foo"),
    person: {
        forename: ko.observable("John"),
        surname: ko.observable("Smith")
    }
};

但是我需要每个属性都是一个可观察的(在这个例子中person也必须是一个obervable)因为我有一些可以处理复杂的可观察对象的自定义绑定。 如何配置映射插件以将每个属性转换为可观察的?

我知道我可以使用这样的东西:

var mapping = {
    'person': {
        create: function (options) {
            var myPersonData = ko.mapping.fromJS(options.data);
            return ko.observable(myPersonData);
        }
    }
};

ko.mapping.fromJS(viewmodel, mapping);

但我需要一个动态的解决方案,因为我不知道属性名称(例如" person")。服务器生成一个动态json对象并将其返回给客户端(带有绑定的HTML代码也是使用正确的属性名称动态创建的)。 必须对json对象中的每个属性执行上面显示的函数 - 这可能吗?或者有替代解决方案吗?

1 个答案:

答案 0 :(得分:1)

我并不完全相信这是多么合理,但作为一个粗略的解决方案,它似乎做了你想做的事。

基本上,遍历javascript数据对象上的所有属性,检查它们是否属于您的属性(使用hasOwnProperty())。然后,您需要查看属性的类型,如果它是一个对象,则需要递归调用makeObservable方法,否则将该属性创建为observable。

我没有实现所有类型,只有字符串,数组和对象,所以你需要填写其余类型:

function makeObservable(data, viewmodel) {
    var vm = viewmodel || ko.observable({});
    for (var prop in data) {
        if (data.hasOwnProperty(prop)) {
            var ctorType = data[prop].constructor;
            var mappedProperty;
            if (ctorType === String) {
                mappedProperty = ko.observable(data[prop]);
            } else if (ctorType === Array) {
                mappedProperty = ko.observableArray();
                for (var arr in data[prop]) {
                    if (data[prop].hasOwnProperty(arr)) {
                        mappedProperty.push(makeObservable(data[prop][arr]));
                    }
                }
            } else if (ctorType === Object) {
                mappedProperty = makeObservable(data[prop]);
            }

            if (ko.isObservable(vm)) {
                vm()[prop] = mappedProperty;
            } else {
                vm[prop] = mappedProperty;
            }
        }
    }
    return vm;
}

var viewModel = makeObservable(data, {});

我还创建了一个jsFiddle