3

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.

NickNuke
  • 303
  • 2
  • 10
  • What is the point of having `optionSet` when it is just returning whatever is in `masterOptions`? – rwisch45 May 16 '14 at 23:52
  • You don't have to duplicate them as "verbosely" ... you could do it [programmatically](http://jsfiddle.net/origineil/3vdu6/6/). But as already stated, it isn't quite clear what you are trying to achieve. Describe that and it might help in answering your question through an alternative approach. – Origineil May 17 '14 at 05:52
  • The ONLY point of having the optionSet object is so that I can have the obptionSet.subscribe function fire any time one of the lower level observables changes. I haven't figured out a way to call the function within the subscribe without creating the optionSet object. – NickNuke May 19 '14 at 16:13

2 Answers2

2

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

Ziad
  • 1,036
  • 2
  • 21
  • 31
0

You can try to use ko mapping plugin. I'm not sure my solution is the best, but you can look at it:

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 />
    span: <span data-bind="text: json"></span><br/>
    <br />
    span2: <span data-bind="text: magicString"></span>
</div>

JS

var anyData = {
    group1: {
        setting1a: ko.observable("value1a"),
        setting1b: ko.observable("value1b")
    },
    group2: {
        setting2a: ko.observable("value2a"),
        setting2b: ko.observable("value2b")
    }
};

var ViewModel = function (data) {
    var magicString = ko.observable("");

    var masterOptions = ko.computed(function() {
        return ko.mapping.fromJS(data);
    });
    masterOptions.subscribe(function () {
        magicString(magicString() + "more magic. ");
    });

    var json = ko.computed(function() {
        return ko.mapping.toJSON(masterOptions);
    });

    return {
        masterOptions: masterOptions,
        magicString: magicString,
        json: json
    };
};

ko.applyBindings(new ViewModel(anyData));

jsfiddle

Max
  • 93
  • 2
  • 7