1

I'm trying to use knockout.js on localhost but it doesn't appear to be loading/working properly and I can't figure out what's going on. Upon loading the text appears but none of the knockout functionality is there. Basically it's just text and I can't modify anything.

I opened up the console and I'm getting the following error in reference to knockout.js

In chrome the error is

Uncaught TypeError: Cannot read property 'nodeType' of null knockout.js:12
Y knockout.js:12
L.b.Da knockout.js:58
(anonymous function)

In firefox the error is

[00:26:57.685] TypeError: f is null @ http://localhost/knockout_test/knockout.js:49

I tried 3 different versions of knockout and all result in the same error.

I'm trying out the airline meals example from the knockout website. I have 4 files all in the same folder. I wasn't sure if I needed jQuery in there so I included that too

meals.html

meal_info.js

knockout.js

jquery.js

this is meal_info.js

// Class to represent a row in the seat reservations grid
function SeatReservation(name, initialMeal) {
    var self = this;
    self.name = name;
    self.meal = ko.observable(initialMeal);

    self.formattedPrice = ko.computed(function() {
        var price = self.meal().price;
        return price ? "$" + price.toFixed(2) : "None";        
    });    
}

// Overall viewmodel for this screen, along with initial state
function ReservationsViewModel() {
    var self = this;

    // Non-editable catalog data - would come from the server
    self.availableMeals = [
        { mealName: "Standard (sandwich)", price: 0 },
        { mealName: "Premium (lobster)", price: 34.95 },
        { mealName: "Ultimate (whole zebra)", price: 290 }
    ];    

    // Editable data
    self.seats = ko.observableArray([
        new SeatReservation("Steve", self.availableMeals[0]),
        new SeatReservation("Bert", self.availableMeals[0])
    ]);

    // Computed data
    self.totalSurcharge = ko.computed(function() {
       var total = 0;
       for (var i = 0; i < self.seats().length; i++)
           total += self.seats()[i].meal().price;
       return total;
    });    

    // Operations
    self.addSeat = function() {
        self.seats.push(new SeatReservation("", self.availableMeals[0]));
    }
    self.removeSeat = function(seat) { self.seats.remove(seat) }
}

ko.applyBindings(new ReservationsViewModel());

this is meals.html

<!--meals.html-->
<script type="text/javascript" src="knockout.js"></script>
<script type="text/javascript" src="meal_info.js"></script>

<h2>Your seat reservations (<span data-bind="text: seats().length"></span>)</h2>

<table>
    <thead><tr>
        <th>Passenger name</th><th>Meal</th><th>Surcharge</th><th></th>
    </tr></thead>
    <tbody data-bind="foreach: seats">
        <tr>
            <td><input data-bind="value: name" /></td>
            <td><select data-bind="options: $root.availableMeals, value: meal, optionsText: 'mealName'"></select></td>
            <td data-bind="text: formattedPrice"></td>
            <td><a href="#" data-bind="click: $root.removeSeat">Remove</a></td>
        </tr>    
    </tbody>
</table>

<button data-bind="click: addSeat, enable: seats().length < 5">Reserve another seat</button>

<h3 data-bind="visible: totalSurcharge() > 0">
    Total surcharge: $<span data-bind="text: totalSurcharge().toFixed(2)"></span>
</h3>
user1406951
  • 454
  • 1
  • 10
  • 28

3 Answers3

4

You need to load Knockout before applying bindings or calling any Knockout functions, so change this:

<script type="text/javascript" src="meal_info.js"></script>
<script type="text/javascript" src="knockout.js"></script>

To this:

<script type="text/javascript" src="knockout.js"></script>
<script type="text/javascript" src="meal_info.js"></script>

If you open a Javascript debug console you'll probably see an error about "ko" not being defined.

Update based on your edit - Your revision to the question sort of obscures what I suspect the issue now is. You should be applying knockout after the HTML, not before. Related question - Getting "Cannot read property 'nodeType' of null" when calling ko.applyBindings

Community
  • 1
  • 1
Snixtor
  • 4,239
  • 2
  • 31
  • 54
  • Hmm, actually I just opened up and it's showing an error on knockout saying "Uncaught TypeError: Cannot Read Property 'nodeType' of null". I'll try a different version – user1406951 Apr 05 '13 at 04:09
  • @user1406951 try it after snixtor's fix – basarat Apr 05 '13 at 04:09
  • @Basarat Ali I did and nothing happened. I'm still getting the uncaught TypeError on the knockout file. I tried 3 different versions of knockout and all are giving me the error. – user1406951 Apr 05 '13 at 04:11
  • What is the line number for this error shown in the console. It should show you the filename as well. – basarat Apr 05 '13 at 04:13
  • @BasaratAli the error is [00:12:04.740] TypeError: f is null @ http://localhost/knockout_test/knockout.js:55. I tried 3 different versions of knockout and all are doing this. – user1406951 Apr 05 '13 at 04:13
  • @Snixtor I updated my original post. Can you please take a look? – user1406951 Apr 05 '13 at 04:32
  • @user1406951 You could try setting a breakpoint in your JavaScript developer/debugger tools on knockout.js line 49 or whichever the appropriate line is from the error msg (better yet, a conditional breakpoint for `f === null` or `!f`) and then look at the stack trace to see what's calling it. If you don't use the conditional breakpoint, you might get a lot of other legitimate calls to it and you'll have to manually inspect the value of `f` in each call until you find one where it's `null` that would result in your `TypeError`. – ajp15243 Apr 05 '13 at 04:40
  • @ajp15243 that's a bit beyond me. How can I set a conditional breakpoint? – user1406951 Apr 05 '13 at 04:41
  • What debugging tools are you using? Firebug? It seems Firefox also has its own [built-in debugging tool](https://developer.mozilla.org/en-US/docs/Tools/Debugger) now, so I'm not sure which you're using. That guide tells you how to use the built-in debugger including using conditional breakpoints. For Firebug, go to the Script tab, choose your script (knockout.js) from the dropdown on the bar just under the Script tab, then right click the line you want to set a breakpoint at. A little dialog pops up, and you put an expression in there. If the expression evaluates to true, the breakpoint fires. – ajp15243 Apr 05 '13 at 04:48
  • I should say right click the line *number*. When a breakpoint is hit, the right-side panel's Stack tab shows you a list of the different JavaScript functions that have been called to reach the line of code your breakpoint is sitting at. Perhaps you can see your own functions in that list, and see if they have incorrect values. Clicking on the function name in the stack trace list will jump your "current execution scope" to that point, so that anything you type in the console will be as if it is executed at that point in your code. I'll stop rambling about debugger tools, but they are useful! – ajp15243 Apr 05 '13 at 04:51
  • @ajp15243 I'm using chrome and managed to get the conditional breakpoint to work but I'm not quite sure how to "fix" it. It found f===null on line 12, but I have no idea what I'm supposed to set it to. For that matter, why is this even happening? Shouldn't knockout run fine considering I haven't modified it? – user1406951 Apr 05 '13 at 04:55
  • It's likely your meal code is calling knockout functions and passing in an incorrect/null value on accident, when knockout is expecting a non-null value. So while the error actually occurs in knockout's code, the source of the error is highly unlikely to be in knockout. The purpose of looking at the stack trace is to see what parts of your code call that part of knockout that gets the error. Then you can go look very closely at those parts of your code to spot the error. – ajp15243 Apr 05 '13 at 04:57
  • @ajp15243 the call stack ends on the meal_info.js final line line of code ko.applyBindings(new ReservationsViewModel()); – user1406951 Apr 05 '13 at 04:58
  • @user1406951 That's because your javascript, particularly `ko.applyBindings` is running *before* your HTML, you need to put the script load **after** the HTML (per the earlier edit on my answer). – Snixtor Apr 05 '13 at 05:00
  • @Snixtor Oh ok, I thought you meant just reverse the two positions, I didn't realize you meant at the bottom of the file. Thanks, it worked – user1406951 Apr 05 '13 at 05:03
  • @user1406951 If you're going to have jQuery on the page, you can wrap your `meal_info.js` code in `$(document).ready(function() { ... });` so that your code doesn't execute until the browser has parsed all the markup on the page. Then you can put your script tags where you like, but they won't be executed until after the page is done loading. Point is, browsers execute JavaScript code as soon as they hit it, not after they've parsed any markup below said JS code. – ajp15243 Apr 05 '13 at 05:11
1

You need the HTML first and then include knockout.js

i.e move the two JS lines to the bottom

Pradeep S
  • 2,289
  • 2
  • 16
  • 6
0

Based on your original code. You are databinding to name but name is not an observable:

<td><input data-bind="value: name" /></td>

and

self.name = name;
basarat
  • 261,912
  • 58
  • 460
  • 511