3

I am using KnockoutJS and I would like to know of a way where a observable object in an observableArray can notify the parent of a change. Here is an example:

http://jsfiddle.net/paragnair/CEEZ5/

HTML:

<h1 id="heading"> <text data-bind="text:childrenCount"></text> Fields selected</h1>
<table id="form">
<tbody data-bind="foreach:children">
    <tr>
        <td data-bind="text:name"></td>
        <td><input type="checkbox" data-bind="checked:isSelected"/></td>
    </tr>
</tbody>
</table>

<a href="#" id="btn-add">Add More Fields</a>​

Javascript:

var Child = function(name) {
    var self = this;
    self.name = ko.observable(name);
    self.isSelected = ko.observable(false);
},
    Parent = function() {
        var self = this;
        self.children = ko.observableArray([
            new Child('One'),
            new Child('Two'),
            new Child('Three')
            ]);
        self.children.subscribe(function(children) {
            header.childrenCount($.map(children, function(a) {                
                return a.isSelected() ? 1 : null;
            }).length);
        });
    },
    header = {
        childrenCount: ko.observable(0)
    };

var parentModel = new Parent(),
    extra = parentModel.children().length;
ko.applyBindings(parentModel, $('#form')[0]);
ko.applyBindings(header, $('#heading')[0]);

function setHeading(childrenCount) {
    header.childrenCount(childrenCount);
}

$(document).ready(function() {
    $('#btn-add').click(function() {
        extra++;
        parentModel.children.push(new Child('Extra ' + extra));
        return false;
    });
});​

In the above example, I want to show the heading with the number of fields selected. I have a subscribe event for the observableArray but that only fires when something is added or removed from the array so when the user actually checks a checkbox in the fields list, the event is not fired. One way of achieving this is by adding a onchange event on the checkbox to call a method on parent which inturn calls some external method which updates the childrenCount on the header object. Is there a better way of doing this?

4

2 回答 2

6

我认为你可以用不同的方式来解决这个问题,同时消除不必要的复杂性。您不需要将模型分成单独的 applyBindings 调用。事实上,除非绝对必要,否则不建议这样做,因为它会增加复杂性,如果您不小心,可能会导致双重绑定。

其次,如果您的标头是您的 parentModels 孩子的结果,那么它确实应该是同一模型的一部分,OOP 原则可以而且应该在编写 KO 模型时应用。这是一开始就选择使用KO优势。

以下是我将如何解决您的问题。

http://jsfiddle.net/madcapnmckay/edJyp/

var Child = function(name) {
    var self = this;
    self.name = ko.observable(name);
    self.isSelected = ko.observable(false);
},
Parent = function() {
    var self = this;
    self.children = ko.observableArray([
        new Child('One'),
        new Child('Two'),
        new Child('Three')
        ]);

    this.childrenCount = ko.computed(function() {
        var count = 0;
        ko.utils.arrayForEach(self.children(), function (child) {
            if (child.isSelected()) {
                count++;
            }
        });
        return count;
    });

    this.addMore = function () {
        self.children.push(new Child("Extra " + (self.children().length + 1)));
    };
};

var ViewModel = function () {
    var self = this;
    this.parentModel = new Parent();  
};

ko.applyBindings(new ViewModel());

请注意 ViewModel 类是多余的,但我假设您拥有的功能比此处显示的更多,这些功能将被标记到此。

顺便说一句,为什么你觉得你需要在你的按钮上使用一个 jquery 点击处理程序?我经常看到这个错误,并且想知道文档中的哪些内容会引导您走上这条路?这也可以而且应该移动到您的 viewModel 上,我在示例中已经这样做了。

希望这可以帮助。

于 2012-05-29T18:11:14.153 回答
2

最后我没有使用 subscribe 而是使用了eventon data-bind,而后者又调用了外部函数setHeading。这是更新的小提琴:http: //jsfiddle.net/paragnair/CEEZ5/5/

于 2012-06-25T17:16:22.277 回答