Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Knockout binding is undefined for click

Tags:

knockout.js

I have a view model Which has to be attached to a click event of an <li> tag. Here is the viewmodel and markup

  var viewModel =
    {
        Folders: ['Inbox', 'Archive', 'Sent', 'Spam'],
        SelectedFolder: ko.observable('Inbox'),
        chosenFolderId: ko.observable(),
        navigate: function () {
            self.chosenFolderId(folder);               
        }
    };
ko.applyBindings(viewModel);

And the markup is

   <ul class="nav nav-list bs-docs-sidenav affix" data-bind="foreach:Folders">
                @*<li data-bind="css:{active: $data == chosenFolderId() }">*@
                <li>
                    <a href="#" data-bind="click:navigate">                      
                        <!-- ko text: $data -->
                        <!-- /ko -->
                        <i class="icon-chevron-right"></i>
                    </a>
                </li>                   
            </ul>

The problem is in this line

<a href="#" data-bind="click:navigate"> 

and

<li data-bind="css:{active: $data == chosenFolderId() }">

Both The line above is not getting attached to the Navigate function and chosenFolderId observable respectively. It says Navigate is undefined . It cannot be parsed. Same goes forchosenFolderId`.

Any idea why is it happening ?

like image 655
Joy Avatar asked Dec 19 '12 04:12

Joy


People also ask

How to bind click event in Knockout?

The function you want to bind to the element's click event. You can reference any JavaScript function - it doesn't have to be a function on your view model. You can reference a function on any object by writing click: someObject. someFunction .

What is binding in knockout?

A binding context is an object that holds data that you can reference from your bindings. While applying bindings, Knockout automatically creates and manages a hierarchy of binding contexts. The root level of the hierarchy refers to the viewModel parameter you supplied to ko. applyBindings(viewModel) .


1 Answers

You have a few problems with your current approach:

When you use a foreach binding e.g data-bind="foreach:Folders" inside your ul the "currect context" will be the items in your folder collection.

So if you want to access the navigate or the chosenFolderId methods you need to use $parent, or $root to access the your "root" viewmodel (you can read more about the binding context):

<ul class="nav nav-list bs-docs-sidenav affix" data-bind="foreach:Folders">
    <li data-bind="css:{active: $data == $parent.chosenFolderId() }">
        <a href="#" data-bind="click: $parent.navigate">                      
            <!-- ko text: $data -->
            <!-- /ko -->
            <i class="icon-chevron-right"></i>
        </a>
    </li>                   
</ul>​

You have some problem in your view model as well. If you have complex functions like navigate which tries to use self you should use a function as a viewmodel instead of an object literal where you can store this:

var viewModel = function() {
    var self = this;
    self.Folders = ['Inbox', 'Archive', 'Sent', 'Spam'];
    self.SelectedFolder = ko.observable('Inbox');
    self.chosenFolderId = ko.observable();
    self.navigate = function(folder) {
        self.chosenFolderId(folder);
    }
};
ko.applyBindings(new viewModel());​

Note that: your navigate function needs a folder parameter to make it work and Knockout will pass the current item for you.

Here is a working JSFiddle.

If you want to do it with an object literal as your view model here is JSFiddle which demonstrates that approach.

However you should be aware what are the strength and weeknesses of the two view model creation approach. This SO question summarizes it well: Difference between knockout View Models declared as object literals vs functions

like image 169
nemesv Avatar answered Sep 24 '22 11:09

nemesv