Here is a jsFiddle which demonstrates my problem: http://jsfiddle.net/dDEd5/4/
In summary, I have a simple ViewModel:
ViewModel = function () {}
ViewModel.prototype = {
child: function () {},
children: new Array(3),
outermethod: function () {
this.innerMethod();
},
innerMethod: function () {
alert("ok!");
},
outerProperty: function () {
return this.innerProperty();
},
innerProperty: function() {
return "Property is OK";
}
}
I am attempting to bind this ViewModel using a ‘click’ binding. The problem is that when my binding uses the $parent context, the value of ‘this’ within my ViewModel fails to resolve to the ViewModel.
For example, this binding works fine:
<div>
<span data-bind="text: outerProperty()"></span>
<button data-bind="click: outermethod">This Works</button>
</div>
However, when I use another binding context and attempt to call my ViewModel using $parent, things break down. In the following two example, the property resolves fine; however, the buttons both error out:
<div>
<!-- ko with: child -->
<span data-bind="text: $parent.outerProperty()"></span>
<button data-bind="click: $parent.outermethod">This Doesn't</button>
<!-- /ko -->
</div>
and
<div>
<!-- ko foreach: children -->
<span data-bind="text: $parent.outerProperty()"></span>
<button data-bind="click: $parent.outermethod">These Don't Either</button>
<!-- /ko -->
</div>
I have done my due diligence trying to understand how Execution Contexts work in javascript and why these examples fail; however, I am at at loss on this.
When Knockout executes a handler it uses the current data bound at that level as the context. So, when using something like
$parentor$root, this can cause issues.There are a few ways to handle it:
-you can bind it to the proper context in the binding itself like:
This returns a new function that ensures
$parentwill bethis.-you can bind it in your view model. Since you are placing the function on the prototype it is a little more challenging.
One technique (not using prototype) is to use a variable to track the correct value of
thisand reference it in your function like:Using the prototype, you can still put the implementation on the prototype and then create a bound version on the actual instance like:
So, this will create a new function on the instance that call the prototype’s implementation of the function with the correct context.