Typescript incorrect this context

the code:

export class ViewModel { public users: knockout.koObservableArrayBase; constructor () { this.users = ko.observableArray([]); this.removeUser = this.removeUser.bind(this);//<-- Here compiller shows error } removeUser(user: User): void { this.users.remove(user); } } 

Html:

 <table> <thead> <tr> <th>Name</th> <th>Surname</th> </tr> </thead> <tbody data-bind="foreach: users"> <tr> <td><a href="#" data-bind="click: $root.removeUser">Remove</a></td> <td data-bind="text: name"></td> <td data-bind="text: surname"></td> </tr> </tbody> </table> 

The problem is the removeUser method. By default, if I do not bind the context, this object == UserToDelete - not viewModel. If I add to the constructor: this.removeUser = this.removeUser.bind(this); (manually enforce context) this.removeUser = this.removeUser.bind(this); (manually enforce context) , then the context should be used as needed in this mode == viewmodel, but then TypeScript complains about "It is impossible to convert the function to (user: User) => void requires call signatures, but the function is not enough."

+7
source share
5 answers

I am not familiar with ko, so there may be a better way to allow context switching, but your typescript compiler error is caused by 'bind' returning a type of 'Function' that is incompatible with the type of 'removeUser'. You should solve this problem by translating the returned function to the original type signature as follows:

 this.removeUser = <(user: User) => void> this.removeUser.bind(this); 
+5
source

Well, I had the same problem, so I came up with the following base class to fix my problem.

 export class ViewModelBase { private prefix: string = 'On'; public Initialize() { for (var methodName in this) { var fn = this[methodName]; var newMethodName = methodName.substr(this.prefix.length); if (typeof fn === 'function' && methodName.indexOf(this.prefix) == 0 && this[newMethodName] == undefined) { this[newMethodName] = $.proxy(fn, this); } } } } 

what it does is a loop of all members of your class, and if the method starts with On, it will create a new method without On, which will call the original method with the correct context.

Not that $.proxy is a jquery call, so it requires jquery.

+2
source

An alternative is to change the click binding to use the JavaScript bind function to force this value for your view model: data-bind="click: $root.MyFunc.bind($root)" .

Note that the $data and click event object will still be passed as knockout arguments to MyFunc , as described in the binding binding . If you need to override the arguments passed to MyFunc , just pass them to the binding function after $root as follows: .bind($root, param1, param2) . Technically, these arguments will be added to the arguments provided by Knockout, giving the arguments [param1, param2, data, event] .

+2
source

Well, the simplest solution and what I usually do with typescript and js knockout is that I do not declare the functions called by the knockout on the prototype, but the constructor. So, I would do this:

 export class ViewModel { public users: knockout.koObservableArrayBase; removeUser:(user: User) => void; constructor () { this.users = ko.observableArray([]); this.removeUser = (user:User) => { this.users.remove(user); } } } 
+1
source

I ran into the same problem. To get the right context, you can use parameters that are passed using bindings. Click binding skips 2 parameters, i.e. user and click jquery event.

If you take the jquery event and use the ko.contextFor () function, you can get the correct context.

Your function will look something like this:

 removeUser(user: User, clickEvent: any): void { var self = ko.contextFor(clickEvent.srcElement).$root; self.users.remove(user); } 
0
source

All Articles