6
votes

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 ?

1

1 Answers

14
votes

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