Dirty Tracking

by Camille Laborde

HTML

<script src="http://cdnjs.cloudflare.com/ajax/libs/knockout.mapping/2.3.5/knockout.mapping.js"></script>
<p>Text property: <input data-bind="value: someTextProperty" /></p>
<p>Bool property: <input type="checkbox" data-bind="checked: boolProperty" /></p>
<p>Array items count: <span data-bind="text: arrayItems().length"></span> <button data-bind="click:addItem">Add</button></p>

<hr/>

<p>Something has changed? <b data-bind="text: tracker().somethingHasChanged() ? 'YES' : 'NO'"></b></p>
<button data-bind="click: tracker().markCurrentStateAsClean, enable: tracker().somethingHasChanged">
    Mark current state as clean
</button>

JavaScript

function changeTracker(objectToTrack, hashFunction) {    
    hashFunction = hashFunction || ko.toJSON;
    var lastCleanState = ko.observable(hashFunction(objectToTrack));
    
    var result = {
        somethingHasChanged : ko.dependentObservable(function() {
            return hashFunction(objectToTrack) != lastCleanState()
        }),
        markCurrentStateAsClean : function() {
            lastCleanState(hashFunction(objectToTrack));   
        }
    };
    
    return function() { return result }
}

var viewModel = {
    someTextProperty: ko.observable("Hello"),
    boolProperty: ko.observable(false),
    arrayItems: ko.observableArray([]),
    
    addItem : function() { this.arrayItems.push("Another") }
};
viewModel.tracker = new changeTracker(viewModel);

ko.applyBindings(viewModel);