Search code examples
knockout.jsparentsubscribecomputed-observable

knockoutjs child object trigger change in parent computed


Is there an easier way to get a parent object's 'subscribe' to fire with changes to any of the lower level observables?

The following code and sample fiddle are working, but it requires me to duplicate my masterOptions in optionSet. This smaller version is manageable, but my masterOptions set can become quite large which will make maintenance of both masterOptions and optionSet difficult and error-prone.

Sample jsfiddle found here: fiddle

html:

<div>
    Setting1a: <input data-bind="value: masterOptions.group1.setting1a" /><br />
    Setting1b: <input data-bind="value: masterOptions.group1.setting1b" /><br />
    Setting2a: <input data-bind="value: masterOptions.group2.setting2a" /><br />
    Setting2b: <input data-bind="value: masterOptions.group2.setting2b" /><br />
    <br />
    span1: <span data-bind="text: ko.toJSON(optionSet)"></span><br/>
    <br />
    span2: <span id="mySpan"></span>
</div>

Script:

var masterOptions = {
    group1: {
        setting1a: ko.observable("value1a"),
        setting1b: ko.observable("value1b")
    },
    group2: {
        setting2a: ko.observable("value2a"),
        setting2b: ko.observable("value2b")
    }
};
var optionSet = ko.computed(function () {
    return {
        group1: {
            setting1a: masterOptions.group1.setting1a(),
            setting1b: masterOptions.group1.setting1b()
        },
        group2: {
            setting2a: masterOptions.group2.setting2a(),
            setting2b: masterOptions.group2.setting2b()
        }
    };
});
optionSet.subscribe(function () {
    //alert("Make some magic happen.");
    $("#mySpan").html($("#mySpan").html() + "more magic. ");
});
var ViewModel = function () {
    return {
        masterOptions: masterOptions,
        optionSet: optionSet
    };
};

ko.applyBindings(new ViewModel());

I would rather NOT have to recreate the masterOptions as optionSet, but I don't see how I can have a single subscribe fire when any of the lower level observables change.


Solution

  • Calling ko.toJS on masterOptions inside your computed will do the trick:

    var optionSet = ko.computed(function () {
        return ko.toJS(masterOptions);
    });
    

    [ jsfiddle ]

    This creates a dependency on every observable in the structure. Also any observables added to it will be included as dependencies.

    However if you want to subscribe directly to masterOptions and/or need more flexibility you might consider using this handy little plugin which I wrote myself for pretty much the same purposes:

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