1

Consider this typescript class (but I don't think Typescript is relevant to the problem, other than obscuring the underlying defineProperty call):

class Model
{
   public TeamId: number;

   constructor()
   {
        var self = this;

        ko.track(this);

        ko.getObservable(this, "TeamId").subscribe(function (newValue)
        {
            var o = ko.getObservable(self, "link");
            o.valueHasMutated();
        });
    }

    get link(): string
    {
        return `/blah/blah/team/${this.TeamId}`;
    }
}

Note that I'm using the mapping plugin.

The link property uses the current TeamId to make a suitable link for that team. My problem is: how do I tell knockout that when the TeamId property changes, so does the link property? I thought that the subscribe callback would do it, but getObservable returns null. I guess this is because it doesn't work with properties defined with defineProperty. But now I'm stuck because I want to use this syntax but can't make it work.

Timh
  • 492
  • 3
  • 11
Joshua Frank
  • 13,120
  • 11
  • 46
  • 95

2 Answers2

0

I would say you should make link a computed.

ko.defineProperty(this, 'link', function () {
  return `/blah/blah/team/${this.TeamId}`;
});

This way it should be updated automatically since you create a dependency on TeamId.

Timh
  • 492
  • 3
  • 11
0

What I do when I want to use property getters/setters is use them with a private backing observable field. That way it hides the observable to the outside, but you still have access to a standard observable internally to subscribe against.

private _property = ko.observable('myValue');
get property(): string { 
    return this._property(); 
}
set property(value:string) {
    this._property(value);
}

In your case you could combine that with a computed to handle the value updating automatically, and I see now that you're using the ES5 plugin which explains the ko.getObservable() and ko.track() calls, so a computed just looks like a regular function. I can't easily test all of that, but see if this does what you want:

class Model
{
   public TeamId: number;

   constructor()
   {
        var self = this;    
        ko.track(this);
    }

    get link(): string
    {
        return this.getLink();
    }

    private getLink(){
        return `/blah/blah/team/${this.TeamId}`;
    }
}
Jason Spake
  • 4,293
  • 2
  • 14
  • 22
  • Based on my current workaround, I think this will work, but it's kind of kludgy to have to make a backing function like that for every property. kind of defeats much of the purpose of *having* properties. – Joshua Frank Mar 13 '17 at 18:36
  • No argument there. You might want to open another ES5-specific question to see if there's a better way. – Jason Spake Mar 13 '17 at 18:41