Reputation: 4458
I am trying to bind a click event to a list item. When I click on the list item below, it should fire up the updateCurrent
function in the javascript, but it doesn't do anything.
note: I have used $parent.updateCurrent
as I am in the currentCat
context when I call the function and the updateCurrent
function is in the ViewModel
context.
HTML:
<div id="wrap" data-bind="with: currentCat">
<div id="names">
<ul data-bind="foreach: $parent.catNames">
<!--Here, clicking on name doesn't fire updateCurrent-->
<li data-bind="text: $data, click: $parent.updateCurrent"></li>
</ul>
</div>
</div>
JS:
var viewModel = function() {
var self = this;
this.catNames = ko.observableArray([]);
this.catList = ko.observableArray([]);
//cats is defined as a simple array of objects, having name
cats.forEach(function(catObject) {
self.catList.push(new cat(catObject));
self.catNames.push(catObject.name);
});
//initially, set the currentCat to first cat
this.currentCat = ko.observable(this.catList()[0]);
//should run when the user click on the cat name in the list
this.updateCurrent = function() {
console.log("hello");
}
};
I don't get any error when I click on the list name, but console doesn't log anything.
I believe I am not using the context properly, but can't figure out the exact issue here. Any help will be appreciated.
Upvotes: 2
Views: 4422
Reputation: 43881
You are inside two levels of context. The with
binding indicates that $data
inside that div
is currentCat
. The foreach
provides another level of context. $parent
doesn't refer to the parent of an object, but the context outside this context. You could use $parents[1]
.
When you say $parent.catNames
, you're only inside the with
context, so it does refer to catNames
in ViewModel
, since there's no other enclosing context. But where you say $parent.updateCurrent
, you're jumping out of the foreach
context up to the with
context. And you wanted to jump one more.
Upvotes: 0
Reputation: 245
When you say $parent.catNames, you are referring to the ViewModel because it is an owned property of the immediate parent in the hierarchy. With the foreach binding, you are within a new context; therefore in $parent.updateCurrent, the immediate parent in the hierarchy becomes the item in catNames array.
You can use $root in this case as Viktor has mentioned but the safest and proper way to achieve this functionality is to climb the hierarchy one level at a time to obtain $parents[1].
$parent is equal to $parents[0] which is one of the catNames. $parents[1] will refer to the ViewModel.
Why not use $root?
Knockout will maintain your DOM and binding context much deeper than may be readily apparent. In template bindings, the $root context may refer to an object that isn't even in the HTML template!
<div data-bind="template: { name: 'sample', data: catNames }"></div>
<script type='text/html' id='sample'>
<!-- ko foreach: $data -->
<span data-bind="text: 'Cat ' + $index + ' of ' + $parents[0].length"></span>
Returns "Cat 1 of 10"
<!-- /ko -->
<!-- ko foreach: $data -->
<span data-bind="text: 'Cat ' + $index + ' of ' + $root.length"></span>
Return "Cat 1 of undefined" because ViewModel has no length property!
<!-- /ko -->
</script>
Binding context documentation: http://knockoutjs.com/documentation/binding-context.html
Upvotes: 4