Knockout.js子对象仅在更新父级并将值设置为子对象时更新

时间:2012-11-08 03:26:45

标签: javascript knockout.js

首先,jsfiddle链接http://jsfiddle.net/wenbert/m5pHs/36/

<button data-bind="click: addHero">Add Hero With Meta</button>

<ul data-bind="foreach: heroes">
    <li class="parent" data-bind="ifnot: isDeleted">
        <input class="big-box" type="text" data-bind="value: name" />
        <button class="btn-small" data-bind="click: $parent.removeHero">Remove Hero</button>
        <br/>
        &nbsp;&nbsp;&nbsp;&nbsp;<button class="btn-small" data-bind="click: $parent.addMeta">Add Meta</button>
        <div class="child" data-bind="template: { name: 'checkbox-template' }"></div>
    </li>
</ul>

<script type="text/html" id="checkbox-template">
    <ul data-bind="foreach: meta">
        <li data-bind="ifnot: isDeleted">
            <input class="child-item blue" type="text" data-bind="value: name" />: 
            <input class="child-item small" type="text" data-bind="value: damage" />
            <button class="btn-small" data-bind="click: $parent.removeMeta">Remove Meta</button>
            <br/>
        </li>
    </ul>
</script>

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

Javascript

var initialData = [
    {
        name: "Batman",
        isDelete: false,
        meta: [
            { name: "Belt", damage: "99", isDeleted: false },
            { name: "Gun", damage: "104", isDeleted: false}
        ]
    },
    {
        name: "Hulk",
        isDelete: false,
        meta: [
            { name: "Pants", damage: "1", isDeleted: false }
        ]
    },
];


function Hero(data) {
    var self = this;
    self.name = ko.observable(data.name);
    self.meta = ko.observableArray(data.meta);
    self.isDeleted = ko.observable(data.isDeleted);
}

function Meta(data) {
    var self = this;
    self.name= ko.observable(data.name);
    self.damage= ko.observable(data.damage);
    self.isDeleted = ko.observable(data.isDeleted);
}

function SuperheroViewModel() {
    var self = this;
    self.heroes = ko.observableArray();
    self.heroes.meta = ko.observableArray();

    self.heroes.push(new Hero(initialData[0]));
    self.heroes.push(new Hero(initialData[1]));

    self.addHero = function() {
        self.heroes.push(
            new Hero({
                name: 'Wolverine',
                isDelete: false,
                meta: [new Meta({name: 'Claws', damage: '200', isDeleted: false})]
            })
        );
        /*
        //Using something like this also does not enable me to update the child items when adding.
        self.heroes.push(new Hero(initialData[1])
        );*/
    }

    self.addMeta = function(item) {
        item.meta.push(new Meta({name: '--', damage: '0', isDeleted: false}));
    }

    self.removeHero= function(item) {
        item.isDeleted(true);
    }

    self.removeMeta = function(item) {
        item.isDeleted(true);
    }

}

ko.applyBindings(new SuperheroViewModel());

看起来像什么

the output

什么有效:

  1. 添加英雄
  2. 删除英雄(将isDeleted设置为true)
  3. 添加元
  4. 更新Hero名称会更新调试视图中的数据
  5. 什么行不通:

    1. 删除元 - 我无法将meta.isDeleted设置为true
    2. 当您更改文本框中的值时,Meta (EG:腰带,枪,裤子)不会更新。但是,如果您编辑Meta,然后编辑父级,则会更新Meta。看起来只有在更新父项时才会触发子项。
    3. 疑难杂症

      1. 如果您添加新英雄然后编辑Meta,即使不更新父项,它们也会自动更新。
      2. 所以2个问题:

        1. 如何使用我在jsfiddle中的内容设置子对象的值? http://jsfiddle.net/wenbert/m5pHs/36/
        2. 如何更新子项目时触发更新?现在,仅当您还更新父(EG:Batman,Hulk)
        3. 时,才会触发子项更新。

          更新:似乎子项目未更新,因为它们是使用initialData array添加的。

          谢谢!

1 个答案:

答案 0 :(得分:3)

好的,请考虑this fiddle中的更改,这些更改有效,我认为更清晰。

我会打破我所做的事情

首先,我通过viewmodels构造函数传递data,以便它可以重用:

ko.applyBindings(new SuperheroViewModel(initialData));

接下来,使用地图构造heroes数组,这样无论传入数据的大小如何,它都会填充:

self.heroes = ko.observableArray(ko.utils.arrayMap(data, function(i){
    return new Hero(i);
}));

我还将meta构造移动到了英雄对象中,因为metaheroes的子项,而不是根视图模型:

self.meta = ko.observableArray(ko.utils.arrayMap(data.meta, function(i){
        return new Meta(i);
    }));

由于hero构造函数处理meta构造,addHero函数不再需要,因此其meta行看起来与initialData行相同:

meta: [{name: 'Claws', damage: '200', isDeleted: false}]

最后,我将add/remove元函数移动到hero视图模型中。这更有意义,它解决了尝试使用$parentmeta的范围问题。

如果您有任何问题,请与我们联系。