In my view model I have prototype methods where the this context is the item in the for each, not the actual view model. How do I achieve this?
<button data-bind="click: $parent.methodThatNeedsAccesToTheParentViewModel">Edit</button>
ViewModel:
ViewModel.prototype.methodThatNeedsAccesToTheParentViewModel= function () {
this = duff //this is the item in the foreach, not the real parent
};
Ideas?
It is highly recommended to use Prototype Method when the object creation is an expensive task in terms of time and usage of resources and already there exists a similar object. This method provides a way to copy the original object and then modify it according to our needs.
An object’s prototype is a private property that each object has that references or points to its prototype (the object that it inherits from). According to the JavaScript documentation at ECMAScript, this internal property is called [ [Prototype]].
By definition, null has no prototype, and acts as the final link in this prototype chain. Nearly all objects in JavaScript are instances of Object which sits on the top of a prototype chain.
This is because objects can inherit properties from their prototype. The object that another object inherits properties from is called its prototype. If you need a mental model, you can think of how a child inherits characteristics from its parent. In this example, the parent would be considered the ‘prototype’ and the child the ‘object’.
Do not pass the function directly to the click binding, wrap it in an anonymous function to have it called in context of your view model:
<button data-bind="click: function() { $parent.methodThatNeedsAccesToTheParentViewModel() }">Edit</button>
this way when the binding is activated it will use $parent as context of the call - i.e. this
It is worth noting (since we can't see the structure of the file bindings) that the $parent variable will refer to the binding preceding the current item being iterated, which is not necessarily the parent of the item in the object graph.
Edit: i was missing some parenthesis above...
Edit 2: this works due to the way the binding is resolved. When the binding is applied by knockout the expression is resolved and must evaluate to a function (and hence why when you bind directly to a function there is no need for the parenthesis). This function is then called when the click binding is activated.
But here's the catch, since the this keyword in prototypal javascript is not bound by the owner of the function (i.e. where it is defined), but rather upon the way it is called (as explained better in mozilla documentation) in this case knockout call the function not through the object that owns it (which would correctly bind the this operator), but binding it explicitly to its current binding context (since it cannot possibly know which instance the function was taken originally). You can replicate this behaviour by getting a reference to your function and then binding it to another object at call time - an example:
A = function() {
this.name = "foo";
};
A.prototype.getName = function() { return this.name; };
a = new A();
console.log(a.getName()); // should log "foo" as expected
f = a.getName; // now f contains a reference to the function
console.log(f()); // this call the function in the current scope - most likely the window, and return nothing
b = new Object(); // another generic object - suppose this is knockout binding context
console.log(f.call(b)); // this bind the call explicitly to b, but since b has no name property, nothing is returned again
console.log(f.call(a)); // this time we bind the function call to an object that has a name proerty, so the correct value is returned
On the other hand, by creating an anonymous function you are calling your method explicitly in the scope of the wanted object ($parent), so this is bound correctly.
I hope that helps a bit... :)
I had the same problem, and I resolved it in this way:
<button data-bind="click: $parent.methodThatNeedsAccesToTheParentViewModel.bind($parent)">Edit</button>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With