2

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.

software_writer
  • 3,941
  • 9
  • 38
  • 64

2 Answers2

4

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

Jonathan
  • 245
  • 1
  • 8
  • 1
    +1 `$root` is rotten and IMO should never have been included (or at least carry a fat health-warning). Use of root effectively means creating a reliance on a form of global scope (usually because the developer didn't bother learn how knockout scopes work). As the project grows, the temptation to use `$root` as the dumping-ground for everything that needs to be in-scope can become very toxic indeed. – spender Feb 23 '16 at 02:17
0

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.

Community
  • 1
  • 1
Roy J
  • 42,522
  • 10
  • 78
  • 102
  • Oh, so when I say `parent.catNames`, parent doesn't refer to ViewModel? then where does it point, currentCat's context? but that's ViewModel, isn't it? – software_writer Feb 22 '16 at 21:55