KnockoutJS subscribe to property changes with Mapping Plugin

14,902

Solution 1

Here's a generic approach based on Ryan Niemeyer's dirty flag.
Click here for the JsFiddle.

Html:

<ol>
<li>
    Telephone : <input data-bind="value: telephone"/>
</li>
<li>
    Address : <input data-bind="value: address"/>
</li>
</ol>​

Javascript:

var model = {
    telephone: ko.observable('0294658963'),
    address: ko.observable('167 New Crest Rd')

};
// knockout extension for creating a changed flag (similar to Ryan's dirty flag except it resets itself after every change)
ko.changedFlag = function(root) {
    var result = function() {};
    var initialState = ko.observable(ko.toJSON(root));

    result.isChanged = ko.dependentObservable(function() {
        var changed = initialState() !== ko.toJSON(root);
        if (changed) result.reset();
        return changed;
    });

    result.reset = function() {
        initialState(ko.toJSON(root));
    };

    return result;
};
// add changed flag property to the model
model.changedFlag = new ko.changedFlag(model);
// subscribe to changes
model.changedFlag.isChanged.subscribe(function(isChanged) {
    if (isChanged)  alert("model changed");
});
ko.applyBindings(model);​

Solution 2

This handy little plugin is pretty close to what you did but it comes with several options and can work over a much broader set of requirements without requiring the Mapping plugin:

https://github.com/ZiadJ/knockoutjs-reactor

Basically it allows you to write this kind of code:

ko.watch(viewModel, function(target, trigger) { 
    // do work
});  
Share:
14,902

Related videos on Youtube

Gabe
Author by

Gabe

Code a lot.

Updated on June 04, 2022

Comments

  • Gabe
    Gabe about 2 years

    Is there anyway I can tell the knockout mapping plugin to subscribe to all property changes call a certain function?

    I realize I can manually subscribe to the property change event in this manner:

    var viewModel = {
        name: ko.observable('foo'),
    }
    
    // subscribe manually here
    viewModel.name.subscribe(function(newValue){
       // do work
    })
    

    I would like to be able to generically subscribe though, since my view models may vary, I don't want to hardcode the property names. I created a function that does this, but it may not be the best approach. It works over all browsers except IE7 and below.

    Here I take a viewmodel as an argument and try to reflect on it subscribing to the properties:

    function subscribeToKO(data) {
    
            $.each(data, function (property, value) {
                if (getType(value) == "Object")
                    data[property] = subscribeToKO(value);
                else if (getType(value) == "Array") {
                    $.each(value, function (index, item) {
                        item = subscribeToKO(item);
                    });
                }
                else {
                    if (value.subscribe) {
                        value.subscribe(function (newValue) {
                            // do work                                         
                        });
                    }
                }
            });
            return data;
        }
    

    Like I said this works, but since I am using the mapping pluging I was hoping there was a hook I could use to provide it with a function that will generically subscribe to property changes.

    Something like:

    mapping = {
       create: function(options){
           options.data.subscribe(function(newValue){
                // do work ???
           });
       }
    }
    
    ko.mapping.fromJS(viewModel, mapping);
    

    Any ideas?