Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Create a dirty flag binding in knockout.js

Tags:

knockout.js

I'm trying to create a binding handler which allows me to track if any of the values used in the binding has changed:

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

So far I tried the following:

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

I was hoping ko.applyBindingsToDescendants would subscribe the observables used in all the bindings within that container. This however doesn't seem to be the case; see http://jsfiddle.net/F3uMr/1/.

Is there another way to achieve this? Or would it be better to create a ViewModel for each container and then use the dirty flag?

like image 747
sroes Avatar asked Nov 01 '22 09:11

sroes


1 Answers

Not sure if this is the best solution, but you can wrap the dirty flag into a 'base class' and keep all of the logic shared between view models there. For example:

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

If you don't want to specify events and just apply the 'dirty' check to all of the observables, you can try something like this: Fiddle

like image 156
Void Ray Avatar answered Nov 08 '22 05:11

Void Ray