When a subclass viewmodel is bound to a View, that overrides a method in a base class in inherits from, "knockout.command" seems to be calling the base method instead of the child overridden method.
Here is a jsfiddle in javascript, although I'm using typescript.
Note a plain old click binding alerts "child action" while the knockout.command alerts "base action". How can I make this correctly call the overridden child method?
Typescript:
class BaseVm {
public commandAction: any;
constructor() {
this.commandAction = ko.asyncCommand({ execute: this.action, canExecute: (isExecuting) => true });
}
public clickAction = () => {
this.action(null, null, null);
}
public action = (val, event, complete) => {
alert("base action");
}
}
class ChildVm extends BaseVm {
constructor() {
super();
}
public action = (loc, event, complete) => {
alert("child action");
}
}
ko.applyBindings(new ChildVm());
HTML:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>TEST</title>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.4/jquery.js"></script>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.3.0/knockout-debug.js"></script>
<script type="text/javascript" src="https://cdn.rawgit.com/CodeSeven/KoLite/master/knockout.command.js"></script>
<script type="text/javascript" src="app.js"></script>
</head>
<body>
<button data-bind="command: { click: $root.commandAction }">COMMAND</button>
<button data-bind="click: $root.clickAction">CLICK</button>
<script>
ko.applyBindings(new ChildVm());
</script>
</body>
</html>
The problem isn't context, it's that base class constructors run before derived class constructors, and it's the derived class constructor where action
gets overwritten by the derived class implementation.
When this line of code runs, this.action
still refers to the base class implementation, and the value is captured at that moment.
constructor() {
this.commandAction = ko.asyncCommand({ execute: this.action, canExecute: (isExecuting) => true });
}
You can instead write this to dynamically get the value of this.action
:
constructor() {
this.commandAction = ko.asyncCommand({ execute: (a, b, c) => this.action(a, b, c), canExecute: (isExecuting) => true });
}