Create a change logger with knockout.js

We have a lot of complex editors on our site.  We often find ourselves needing to track changes within a complex view model.  With the ability to extend knockout observables, you can create some really useful and reusable tools to go along with the already powerful knockout framework.

To begin, you’ll need to extend the knockout observable with a change tracking extension.  The power of extending the observable lies in the ability to hang additional observables off the observable, itself.  With these, we’ll be able to track the observable’s original state and provide an isDirty computed column that can then be interrogated at the view model level.  This version handles numbers and strings pretty well, but you might have to do some more coding to deal with dates or more complicated values.

ko.extenders.trackChange = function (target, track) {
    if (track) {
        target.isDirty = ko.observable(false);
        target.originalValue = target();
        target.subscribe(function (newValue) {
            // use != not !== so numbers will equate naturally
            target.isDirty(newValue != target.originalValue);
        });
    }
    return target;
};

Now, we can add this extension to any observable we want to track like so:

self.MyProperty = ko.observable('My Test Value').extend({ trackChange: true });
self.MyProperty2 = ko.observable('My Test Value 2').extend({ trackChange: true });

… and these properties can now be interrogated by the view model:

self.MyProperty.isDirty()
self.MyProperty2.isDirty()

We can write an outer computed function that iterates over the entire view model looking for changed items… this could probably be cleaner, but it gets the job done:

self.isDirty = ko.computed(function () {
    for (key in self) {
        if (self.hasOwnProperty(key) && ko.isObservable(self[key]) 
            && typeof self[key].isDirty === 'function' && self[key].isDirty()) {
            return true;
        }
    }
    return false;
});

It would be cool to be able to run this on the view model as a whole and it would spider over the entire model, including observable arrays, and track changes all the way down.  If it could be made generic, it would make a nice GitHub project.

Enjoy!

One thought on “Create a change logger with knockout.js

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s