Dirtiable Observable

Basic dirty flag for observables.

by Areson

HTML

<script src="https://raw.github.com/SteveSanderson/knockout/master/build/output/knockout-latest.debug.js"></script>
<input type="text" data-bind="value: observe"></input>
<button data-bind="click: function(){observe.clean()}">Clean!</button>

The value is <span data-bind="text: observe.dirty()?'dirty':'clean'"></span>

<hr/>
<h3>List Example</h3>

<input data-bind="value: listAdd"></input>
<button data-bind="click: addToList">Add to List</button>
<button data-bind="click: observeArray.clean">Clean!</button>
<button data-bind="click: emptyArray">Remove All</button>
The list is <span data-bind="text: observeArray.dirty()?'dirty':'clean'"></span>

<table data-bind="foreach: observeArray()">
    <tr>
        <td data-bind="text: $data"></td>
    </tr>
</table>

JavaScript

/* A fleshed-out implementation of the dirtiable observables
 * based on the the work done by Ryan Niemeyer.  
 * (http://www.knockmeout.net/2011/05/creating-smart-dirty-flag-in-knockoutjs.html)
 */ 
ko.extenders.dirtiable = function(target) {
    var _dirty = {};
    _dirty.isDirty = ko.observable(false);
    _dirty.initialState = null;
    _dirty.tracker = ko.computed(function() {
        if(!_dirty.isDirty())
            ko.toJSON(target);
            
        return(_dirty.isDirty());
    });
    
    //Track changes to observable
    _dirty.tracker.subscribe(function() {
        if(!_dirty.isDirty() && _dirty.initialState !== ko.toJSON(target))
            _dirty.isDirty(true);
    });
    
    //Create the dirty flag on the observable
    target.dirty = ko.computed(function() {
        return(_dirty.isDirty());
    });
        
    //Allow users to reset the dirty flag
    target.clean = function() {
        _dirty.initialState = ko.toJSON(target);
        _dirty.isDirty(false);
    }
    
    return target;
}

var viewModel = {
    observe: ko.observable(42).extend({
        dirtiable: true
    }),
    observeArray: ko.observableArray().extend({
        dirtiable: true
    }),
    listAdd: ko.observable(),
    addToList: function() {
        this.observeArray.push(this.listAdd());
        this.listAdd(null);
    },
    emptyArray: function() {
        this.observeArray.removeAll();
    }
}


ko.applyBindings(viewModel);