0

I am trying to do a simple ko binding but get an error if I do the following:

<div data-bind="foreach: collections">
    .....
</div>

Here is the js code:

define(
    ['jquery', 'knockout', 'RestClient', 'Constants'],

    function($, ko, ccRestClient, Constants) {

        var collections = ko.observableArray([]);

        return {
            onLoad: function() {

                RestClient.request(Constants.ENDPOINT, input,
                    function(data) {
                        for (var i = 0; i < data.childData.length; i++) {
                            var level = {
                                "firstName": ko.observable(data.childData[i].firstName),
                                "Id": ko.observable(data.childData[i].Id)
                            };
                            categories.push(level);
                        }
                    });
            }
        }
    }
);

I get the following error:

Error - collections is not defined

Jeroen
  • 60,696
  • 40
  • 206
  • 339
user2281858
  • 1,957
  • 10
  • 41
  • 82
  • Possible duplicate of [How do JavaScript closures work?](http://stackoverflow.com/questions/111102/how-do-javascript-closures-work) – Jeroen Jan 17 '16 at 21:29
  • @Jeroen, what am i doing wrong here? – user2281858 Jan 17 '16 at 21:31
  • Have you ready through the duplicate I proposed? It's a more elaborate explanation of what @PJDev's answer also explains. (That answer IMO already explains where your specific code goes wrong, and the linked duplicate will help you understand why.) – Jeroen Jan 17 '16 at 21:39
  • struggling to understand – user2281858 Jan 17 '16 at 22:13
  • I appreciate that it can be a difficult topic to grasp, but without understanding closures, you'll keep being mystified by subtle bugs. I highly recommend reading, studying, and experimenting with it until you understand. – Jeroen Jan 17 '16 at 22:30
  • I suggest trying out the Knockoutjs context debugger for Chrome: https://chrome.google.com/webstore/detail/knockoutjs-context-debugg/oddcpmchholgcjgjdnfjmildmlielhof -- It can help determine whether 'collections' is defined in the Knockout context. – Eric Bronnimann Feb 05 '16 at 04:14

3 Answers3

3

With Knockout, you should use something like ViewModel which is simply an object with properties and functions that you will use in the view. I cannot see that in your code. It should look like:

function ViewModel() {
    var self = this;

    self.collections = ko.observableArray();

    // do what you want with collections
}

In view onload function you should use ko.applyBindings(new ViewModel()) to apply all of your bindings to the view. Only then you will be able to access them with data-bind attributes.

UPDATE

If ko.applyBindings is applied internally, the problem is with the way you declare collections. You made it a private variable with var, but it should be made a property of the model which is applied with ko.applyBindings. If object returned by the function in your code is the model, just make it like this:

return {
    collections: ko.observableArray(),
    onLoad: //...
}

If not, then I cannot tell you the solution without more details about your application.

PJDev
  • 951
  • 5
  • 20
  • i am working in an environment where ko.applyBindings(new ViewModel()) is internally handled – user2281858 Jan 17 '16 at 21:04
  • Ok, so I think the problem is with the way you declare collections. You made it a private variable with 'var' without assigning it to any model. You must make it a property of a model which is internally applied with ko.applyBindings. – PJDev Jan 17 '16 at 21:07
  • From the code you provided, I cannot figure out what is wrong. I would need to see what is the model and how and when it's applied to the view. These are the crucial things without which it'll be hard to find a solution... Edit: I'm now wondering: what are the 'categories' in the request callback? – PJDev Jan 17 '16 at 21:35
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/100916/discussion-between-pjdev-and-user2281858). – PJDev Jan 17 '16 at 21:39
0

First of all, you have a mistype in your js code. See the define

function($, ko, ccRestClient, Constants) { 

and then you use RestClient, not ccRestClient from above

RestClient.request(Constants.ENDPOINT, input,

Replace RestClient with ccRestClient.

brainboost
  • 379
  • 2
  • 14
0

As strange as it sounds, removing the use: strict did the trick for me.

user2281858
  • 1,957
  • 10
  • 41
  • 82
  • The "use strict"; declaration prevents JS from implicitly defining global variables, e.g. collections = []; without the 'var', will create a global variable if 'collections' is not defined in scope. Most likely the reason why the code works now is that JS is implicitly creating a 'collections' global variable instead of throwing the error. See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Strict_mode for more information. – Eric Bronnimann Feb 05 '16 at 04:11