How can I get knockout to update a defined property?

173 Views Asked by At

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.

2

There are 2 best solutions below

1
Timh On

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.

2
Jason Spake On

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}`;
    }
}