1
votes

I have made this jsfiddle to illustrate a problem I am having:

http://jsfiddle.net/gt8d2mub/

var model = {
    a: ko.observable(false),
    b: ko.observable(false),
    c: ko.observable(false),
    data: ko.observable(''),
    isValid: function () {
        return this.a() && this.b() && this.c();
    }
};

model.allTrue = ko.computed(function () {
    return this.a() && this.b() && this.c();
}, model);

model.query = ko.computed(function () {
    return 'a = ' + this.a() + '; b = ' + this.b() + '; c = ' + this.c() + ';';
}, model);

model.query.subscribe(function () {
    if (this.isValid()) {
        // load some data...  
        this.data(this.query());
    }
    else {        
        this.data('no value'); 
    } 
}, model);

ko.applyBindings(model);

The model has three boolean observables; a, b and c. A computed observable (allTrue) calculates if they are all true. Another computed observable (query) is a summary of a, b, and c.

If I manually subscribe to "query" with the intention of loading some data, but first check "allTrue", "allTrue" may not yet be updated and correct, because the procedure to update its value happens after the callback fires.

For example, check a, then b, then c and the callback is called before the computed "allTrue" is calculated.

Is what I am trying to do abnormal, or is there a way to get it to work?

Perhaps there is no advantage to using a computed, and indeed, if I replace it with a normal function (isValid) I get the behaviour I expect. But I intend to have other things react to the allTrue/isValid value, and I thought I would usually use a computed in such scenarios (a normal function would also work, but I thought there would usually be advantages to using a computed there).

2

2 Answers

1
votes

I see no reason why you would need to use a manual subscription. If you're not going to allow writing to the data property, you might as well make it computed.

model.data = ko.computed(function () {
    if (this.allTrue()) {
        return this.query();
    }
    else {
        return 'no value';
    }
}, model);
1
votes

If query isn't valid unless all terms are available, it's probably better to push that evaluation into the query observable to tighten up the dependencies:

http://jsfiddle.net/gt8d2mub/1/

var model = {
    a: ko.observable(false),
    b: ko.observable(false),
    c: ko.observable(false),
    data: ko.observable('')
};

model.allTrue = ko.computed(function () {
    return this.a() && this.b() && this.c();
}, model);

model.query = ko.computed(function () {
    return this.allTrue() ? 
        'a = ' + this.a() + '; b = ' + this.b() + '; c = ' + this.c() + ';' 
      : null;
}, model);

model.query.subscribe(function (query) {
    if (!query) {
        this.data('no value'); 
        return;
    }
    // load some data...
    this.data(query);    
}, model);

ko.applyBindings(model);