在knockout.js中创建一个脏标志绑定

时间:2013-12-03 12:12:46

标签: knockout.js

我正在尝试创建一个绑定处理程序,它允许我跟踪绑定中使用的任何值是否已更改:

<div id="container1" data-bind="dirty: $root.container1Dirty">
    <span data-bind="visible: $root.container1Dirty">*</span>

    <label>
        Text 1
        <input data-bind="value: $root.text1" />
    </label>
</div>

到目前为止,我尝试了以下内容:

ko.bindingHandlers.dirty = {
    init: function(element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {

        var counter = 0;
        var dirtyObservable = valueAccessor();
        var appliedBindings = false;

        var computed = ko.computed(function() {
            if (!appliedBindings) {
                // I was hoping this would subscribe all the used observables
                ko.applyBindingsToDescendants(bindingContext, element);
                appliedBindings = true;
            }
            // make sure subscribe is triggered by returning a new value
            return counter++; 
        });
        computed.subscribe(function() {
            dirtyObservable(true);
        });

        return { controlsDescendantBindings: true };
    }
};

我希望ko.applyBindingsToDescendants订阅该容器内所有绑定中使用的observable。然而,情况似乎并非如此;见http://jsfiddle.net/F3uMr/1/

还有另一种方法可以达到这个目的吗?或者为每个容器创建一个ViewModel然后使用dirty flag

会更好

2 个答案:

答案 0 :(得分:1)

不确定这是否是最佳解决方案,但您可以将脏标志包装到“基类”中并保持视图模型之间共享的所有逻辑。例如:

<强> Fiddle is here

<input type='text' data-bind="value: textBox, event: { keyup: isDirty }">
<input type='button' value='click me if you can' data-bind="click: save, ,enable: isDirty"> 

var BaseViewModel = function (saveUrl, data) {
    var self;
    this.isDirty = ko.observable(false);
    this.save = function(){
        var jsData = ko.toJS(data);
        alert('save: ' + jsData.textBox);
    };
};

var pageViewModel = function(){
    var self = this;

    this.textBox = ko.observable('');
    ko.utils.extend(self, new BaseViewModel('url for save', {textBox: self.textBox}));
}

ko.applyBindings(new pageViewModel());  

如果您不想指定事件并且只对所有可观察对象应用“脏”检查,您可以尝试以下方法: Fiddle

答案 1 :(得分:1)

现在我手动提供所有可观察的跟踪:

<div id="container1" data-bind="
    dirty: {
        flag: 'container1Dirty',
        target: [$root.text1]
    }">
    <span data-bind="visible: container1Dirty">*</span>

    <label>
        Text 1
        <input data-bind="value: $root.text1" />
    </label>
</div>

使用以下绑定处理程序:

ko.bindingHandlers.dirty = {
    init: function(element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {

        var data = valueAccessor();
        var dirtyObservable = ko.observable(false);
        var orgValue = ko.toJSON(data.target);

        var extraBinding = {};
        extraBinding[data.flag || 'dirty'] = dirtyObservable;

        var innerBindingContext = bindingContext.extend(extraBinding);
        ko.applyBindingsToDescendants(innerBindingContext, element);

        var dirty = ko.computed(function() {
            return orgValue !== ko.toJSON(data.target);
        });

        dirty.subscribe(function(isDirty) {
            dirtyObservable(isDirty);
        });

        return { controlsDescendantBindings: true };
    }
};

ko.applyBindings({
    text1: ko.observable('text1'),
    text2: ko.observable('text2')
});

请参阅http://jsfiddle.net/ZdS3W/

这不是我希望的解决方案,因为我现在必须手动提供我想跟踪的所有可观察对象,而不是自动查找所有使用过的可观察对象。所以我仍然愿意接受更好的解决方案。