3

I am trying to get to grips with knockout.js, but I seem to be running into a javascript quirck I am not aware of.

Html

<ul data-bind="foreach: someList">
    <li data-bind="text: number"></li>
</ul>

Javascript

function NestedViewModel(number) {
    self = this;
    self.number = ko.observable(number); 
}

function ViewModel() {
    self = this;
    self.someList = [];

    for (i=0;i<10;i++) {
        var vm = new NestedViewModel(i);
        self.someList.push(vm);
    }

}

ko.applyBindings(new ViewModel());

When I run this code, nothing happens. The console shows an error:

Uncaught TypeError: Cannot read property 'push' of undefined

When I remove the self = this; line from the NestedViewModel, and I replace self.number by this.number, everything works fine! I have no clue what javascript is doing under the hood here... Does somebody have an explanation for this? And a solution?

jsFiddle

jkokorian
  • 2,905
  • 7
  • 32
  • 47
  • For a morde detailed explanation about `this`, please have a look here https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/this. – Christos Apr 27 '15 at 21:22

2 Answers2

6

You need to declare those variables by putting var in front of them. Without declaring them, you're trying to write to the global self, which most browsers won't let you do. (If it weren't that there's already a global called self in browsers, that code would be falling prey to The Horror of Implicit Globals and would still want var — in fact, it does later, with i.)

E.g.:

function NestedViewModel(number) {
    var self = this;
//  ^^^
    self.number = ko.observable(number); 
}

function ViewModel() {
    var self = this;
//  ^^^
    self.someList = [];

    for (var i=0;i<10;i++) {
//       ^^^
        var vm = new NestedViewModel(i);
        self.someList.push(vm);
    }

}

ko.applyBindings(new ViewModel());

(Mind you, there's no need for a self variable in the quoted code, but I'm assuming at some point you use a closure and the self becomes useful then.)


It wouldn't have helped with self, but you might want to consider using strict mode, which would have warned you about the undeclared i variable. You do that by putting "use strict" at the top of the script file; more here.

Community
  • 1
  • 1
T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875
  • 1
    Aarrrg! It seems really obvious now. I'm just too used to using typesafe languages so I don't notice these mistakes right away. Hopefully "use strict" is going to solve that, at least partly. Thanks a lot! – jkokorian Apr 27 '15 at 21:29
0

In addition to the answer by @T.J. You will probably want to make the array an observable too...

self.someList = ko.observableArray([]);
Aydin
  • 15,016
  • 4
  • 32
  • 42
  • You're right, but it's nice to be aware that the option is there, perhaps I should have added it as a comment – Aydin Apr 28 '15 at 07:15
  • Indeed, I simplified the example code down to the bare essentials to demonstrate the problem:) – jkokorian Apr 28 '15 at 08:49