3

I'm trying to do something simple using Typescript and knockout, but can't get it to work. As my codebase of typescipt grows, it seems my viewmodels are growing and need te be nicely modeled in main classes and sub classes. Typescript is perfect for it! In combination with knockout I ran into an annoying problem/bug/situation .... Any help appreciated!!! Here's some typescript code:

class subClassA {
  counter  =0;
  incCounter(){
    this.counter++;
    console.log("counter incs: "+this.counter);
  }
}

class MainViewModel {
  a = new subClassA();


  constructor(){
    this.a.incCounter(); // this works...
  }
  incCounterIndirect(){
    this.a.incCounter(); // this works....
  }
}
ko.applyBindings(new MainViewModel() );

HTML:

<a data-bind="click: $root.incCounterIndirect ">Indirect does work</a>
<a data-bind="click: $root.a.incCounter ">Direct does NOT work</a>

Obviously I need the 'direct' route to work, ie .. calling methods on subClasses directly from the data-bind. Otherwise I need to make proxy members on the mainviewmodel for each subclass/member ...

Which binding prefix or whatever other trick could do the job of calling the member of object A from the click handler.

Any help appreciated, Paul

2
  • Can you be more specific? So what do you mean on does work and does not work? What should be the expected output and what happens instead? Commented Sep 23, 2013 at 19:18
  • nemesv, from the click evt handler, i'd like to be able to call the method incCounter on subClassA directly, not using a proxy method on the main viewmodel. Commented Sep 23, 2013 at 19:32

3 Answers 3

5

Use instance members with the fat arrow (introduced in TS 0.9.x) to overcome this scoping issues with prototype members :

class subClassA {
  counter=0;
  incCounter= ()=>{  // Notice difference
    this.counter++;
    console.log("counter incs: "+this.counter);
  }
}

class MainViewModel {
  a = new subClassA();


  constructor(){
    this.a.incCounter(); 
  }
  incCounterIndirect= ()=>{    // Notice difference
    this.a.incCounter(); 
  }
}
ko.applyBindings(new MainViewModel() );
Sign up to request clarification or add additional context in comments.

1 Comment

basarat ... that does it!!! thanx a million. nilgundag was commenting in the same direction, I jusnt couldnt figure out how to put it in typescript. Anyway ... thanx all for the great help!!!!
2

I am not familiar with typescript but i think the answer should be similar to this:

class subClassA {
  counter  =0;
  self = this;
  incCounter(){
    self.counter++;
    console.log("counter incs: "+self.counter);
  }
}

The problem is with the "this" keyword. it gets different values in your two different versions. To ensure it always have the same value, you capture value of this keyword in the self variable and use it.

Here is fiddle for the javascript version: http://jsfiddle.net/nilgundag/ySmw3/

function subClassA() {
    this.counter  = 0;
    var self = this;
    this.incCounter = function(){
        self.counter++;
        console.log("counter incs: "+self.counter);
    }
}

function MainViewModel() {
    this.a = new subClassA();
    this.incCounterIndirect=function(){
        this.a.incCounter(); // this works....
    };
}
ko.applyBindings(new MainViewModel() );

3 Comments

Thanx for your input. I tried to get this in TypeScript. self = this; (works), self.counter++; (Does not work)
I see. This link may help: stackoverflow.com/questions/12756423/…
That example shows indeed the problem, but does not really come up with an elegant solution imho. Simple things are becoming quit complex like that.
1

Rather than change your handlers to use the fat arrow, you can instead just change the way you're binding:

<a data-bind="click: $root.a.incCounterIndirect.bind($root.a)">This will work</a>

This creates a new function whose "this" argument is the view model, which causes the method to behave like an instance method.

Working JSFiddle here: http://jsfiddle.net/Vjknn/

4 Comments

Judah, if changing the handlers could be avoided, it would be nice indeed. The example you gave however, IMHO, does not change a lot ... your snippet does the same as:<a data-bind="click: $root.incCounterIndirect ">Indirect does work</a>
Sorry, I just noticed you're trying to call it on $root.a. I've updated the above code with a working example and fiddle.
Judah, thanx ... hummm that indeed solves the problem, WITHOUT changing the implementation of the handlers ... nice! I will have to figure out which solution I prefer ...
Personally, I prefer to avoid messing with the code and just change the way the binding will call the handler. But, you're free to choose whichever you prefer.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.