204

I keep having trouble with debugging problems in KnockoutJS templates.

Say I want to bind to a property called "items" but in the template I make a typo and bind to the (non existing) property "item".

Using the Chrome debugger only tells me:

"item" is not defined.

Are there tools, techniques or coding styles that help me get more information about the binding problem?

Martin Devillers
  • 17,293
  • 5
  • 46
  • 88
RogierBessem
  • 2,623
  • 3
  • 17
  • 15

13 Answers13

359

One thing that I do quite often when there is an issue with what data is available at a certain scope is to replace the template/section with something like:

<div data-bind="text: ko.toJSON($data)"></div>

Or, if you want a slightly more readable version:

<pre data-bind="text: JSON.stringify(ko.toJS($data), null, 2)"></pre>

This will spit out the data that is being bound at that scope and let you make sure that you are nesting things appropriately.

Update: as of KO 2.1, you can simplify it to:

<pre data-bind="text: ko.toJSON($data, null, 2)"></pre>

Now the arguments are passed on to JSON.stringify.

Jeroen
  • 60,696
  • 40
  • 206
  • 339
RP Niemeyer
  • 114,592
  • 18
  • 291
  • 211
  • ohh. I've need to ask this question too. Used complicated piece of code to console.log data. Now it's much easier. – AlfeG Feb 13 '12 at 15:03
  • Nice tip Ryan! Any more of such debugging tips? If not then I'll mark this as answered :P – RogierBessem Feb 14 '12 at 08:15
  • 3
    I have to think more about debugging tips and maybe make a blog post. Another one that comes to mind is doing manual subscriptions against observables or computed observables to watch values changing. Like if `name` is an observable doing `name.subscribe(function(newValue) { console.log("name", newValue); });` – RP Niemeyer Feb 14 '12 at 14:55
  • 1
    Might be because this answer is relatively old, but why not use console.log and make use of the full power of the debugger for viewing object properties? See i.e.: http://stackoverflow.com/a/16242988/647845 – Dirk Boer Mar 25 '14 at 13:54
  • 1
    @DirkBoer - using console.log can be a great way too. A lot of times I want to see the data next to my elements like in a `foreach` scenario though and I find it easier to see on the page within the relevant rendered markup than sift through the console. Just depends on the situation. Some more of my thoughts here: http://www.knockmeout.net/2013/06/knockout-debugging-strategies-plugin.htmlhttp://www.knockmeout.net/2013/06/knockout-debugging-strategies-plugin.html. Also, you might want to log a "clean" version in your binding like `console.log(ko.toJS(valueAccessor())`. – RP Niemeyer Mar 25 '14 at 18:26
  • @RPNiemeyer your comment from Mar 25 has a strange 'double link' :) I have had some problems with ko.toJSON giving a circular error - but not very consistent. Do you have any idea why that is? – Rune Jeppesen May 06 '14 at 10:07
  • 1
    @RuneJeppesen - I am not sure what kind of data that you are serializing, but something like this can help: http://www.knockmeout.net/2011/04/controlling-how-object-is-converted-to.html – RP Niemeyer May 06 '14 at 13:16
  • You can even do this `data-bind="text: console.log($data)"`, so you can browse binded objects from the console. – Grirg Aug 02 '17 at 10:45
60

If you are using Chrome for development, there is a really great extension (with which I'm not affiliated) called Knockoutjs context debugger that shows you the binding context directly in the Developer Tools' Elements panel.

neverfox
  • 6,680
  • 7
  • 31
  • 40
  • 3
    I wish Firefox or Firebug had this. Anyone know of such a thing? – Patrick Szalapski Jan 02 '14 at 18:25
  • Appears support has been dropped. Causes chrome to crash if you use a complex data-bind structure. Has not worked for any of my projects for roughly a year now. – Arctic Jan 27 '15 at 19:59
  • Sorry to hear it, though I've long since moved on from KO to Ember. – neverfox Jan 31 '15 at 21:57
  • 1
    It works (mostly) fine for me, and I have some really complex structures. I haven't tried it but in the Options for the extension it suggests, "If you experience crashes you propably have a non serializable viewmodel. You can turn off the serialization." There's a checkbox below the message for disabling this feature. – Grinn Apr 03 '15 at 18:53
  • hugely useful instantly, ty. – Andrew Mar 03 '17 at 17:07
41

Define a bindingHandler once, somewhere in your JavaScript library files.

ko.bindingHandlers.debug = 
{
    init: function(element, valueAccessor) 
    {
        console.log( 'Knockoutbinding:' );
        console.log( element );
        console.log( ko.toJS(valueAccessor()) );
    }
};

than simply use it likes this:

<ul data-bind="debug: $data">

Advantages

  • Use the full power of the Chrome debugger, like Reveal in Elements Panel
  • You don't have to add custom elements to your DOM, just for debugging

enter image description here

Dirk Boer
  • 8,522
  • 13
  • 63
  • 111
33

I found another one that can be helpfull. I was debugging some bindings and tried using Ryans example. I got an error that JSON found a circular loop.

<ul class="list list-fix" data-bind="foreach: detailsView().tabs">
 <li>
   <pre data-bind="text: JSON.stringify(ko.toJS($parent), null, 2)"></pre>
   <a href="#" data-bind="click: $parent.setActiveTab, text: title"></a>
 </li>
</ul>

But, using this approach a replaced the data-bind value with the following:

  <ul class="list list-fix" data-bind="foreach: detailsView().tabs">
    <li>
      <pre data-bind="text: 'click me', click: function() {debugger}"></pre>
      <a href="#" data-bind="click: $parent.setActiveTab, text: title"></a>
    </li>
  </ul>

Now if i click on the PRE element while having the chrome debug window open, I get a nicely filled scope variables window.

Found a little better way for it:

<pre data-bind="text: ko.computed(function() { debugger; })"></pre>
RogierBessem
  • 2,623
  • 3
  • 17
  • 15
  • Really useful. Was encountering knockout circular loops and Razor markup issues using
    . The  is a perfect workaround. For some reason RAZOR inputs like @Html.CheckBox was breaking ko.toJSON.
    – Arctic Sep 05 '14 at 20:34
22

Step by step guide

  1. For this guide, we will be using one of the official KnockoutJS examples.
  2. Say you want to see the data behind the second contact (Sensei Miyagi).
  3. Right-click the first input box of the second contact (the one with the text 'Sensei').
  4. Select 'Inspect element'. The Chrome Developer Toolbar will open.
  5. Open the JavaScript Console window. You can access the console by clicking the >= icon in the bottom-left of the Chrome Developer Toolbar, or by opening the "Console" tab in the Chrome Developer Toolbar, or by pressing Ctrl+Shift+J
  6. Type the following command and press Enter: ko.dataFor($0)
  7. You should now see the data that is bound to the second row. You can expand the data by pressing the little triangle left of the Object to navigate the object tree.
  8. Type the following command and press Enter: ko.contextFor($0)
  9. You should now see a complex object that contains the entire Knockout context including the root and all parents. This is useful when you are writing complex binding expressions and you want to experiment with different constructs.

Example output when following above guide

What is this black magic?

This trick is a combination of Chrome's $0-$4 feature and KnockoutJS's utility methods. In short, Chrome remembers which elements you have selected in the Chrome Developer Toolbar and exposes these elements under the alias $0, $1, $2, $3, $4. So when you right-click an element in your browser and select 'Inspect element', this element automagically becomes available under the alias $0. You can use this trick with KnockoutJS, AngularJS, jQuery or any other JavaScript framework.

The other side of the trick is KnockoutJS's utility methods ko.dataFor and ko.contextFor:

  • ko.dataFor(element) - returns the data that was available for binding against the element
  • ko.contextFor(element) - returns the entire binding context that was available to the DOM element.

Remember, Chrome's JavaScript Console is a fully functional JavaScript runtime environment. This means that you are not limited to just looking at variables. You can store the output of ko.contextFor and manipulate the viewmodel directly from the console. Try var root = ko.contextFor($0).$root; root.addContact(); and see what happens :-)

Happy debugging!

Community
  • 1
  • 1
Martin Devillers
  • 17,293
  • 5
  • 46
  • 88
7

Check out a really simple thing I use:

function echo(whatever) { debugger; return whatever; }

Or

function echo(whatever) { console.log(whatever); return whatever; }

Then in html, say, you had:

<div data-bind="text: value"></div>

Just replace it with

<div data-bind="text: echo(value)"></div>

More advanced:

function echo(vars, member) { console.log(vars); debugger; return vars[0][member]; }

<div data-bind="text: echo([$data, $root, $parents, $parentContext], 'value')"></div>

Enjoy :)

UPDATE

Another annoying thing is when you are trying to bind to an undefined value. Imagine in the example above that the data object is just {} not { value: 'some text' }. In this case you will be in trouble, but with the following tweak you will be fine:

<div data-bind="text: $data['value']"></div> 
Trident D'Gao
  • 18,973
  • 19
  • 95
  • 159
5

I created a github project called knockthrough.js to help visualize these errors.

https://github.com/JonKragh/knockthrough

It highlights binding errors and gives a dump of the datacontext on that node.

You can play with a sample here: http://htmlpreview.github.io/?https://github.com/JonKragh/knockthrough/blob/master/default.htm

enter image description here

Credit to RP Niemeyer for his excellent Knockout code samples on SO to get me to this point.

Jon Kragh
  • 4,529
  • 5
  • 26
  • 26
4

The easiest way to see what data are passed to binding is to drop the data to console:

<div data-bind="text: console.log($data)"></div>

Knockout will evaluate value for text binding (any binding can be used here in fact) and flushes $data to console browser panel.

Dmitry Pavlov
  • 30,789
  • 8
  • 97
  • 121
2

All the other answers will work great, I'm just adding what I like to do:

In your view (assuming you've already bound a ViewModel):

<div data-bind="debugger: $data"></div>

Knockout code:

ko.bindingHandlers.debugger = {
    init: function (element, valueAccessor) {
        debugger;
    }
}

This will pause the code in the debugger, and element and valueAccessor() will contain valuable information.

Aditya M P
  • 5,127
  • 7
  • 41
  • 72
  • there is no need for a custom binding. Take a look at http://stackoverflow.com/documentation/knockout.js/5066/debugging-a-knockout-js-application/27325/printing-a-binding-context-from-markup#t=201702201513545237604 – Adam Wolski Feb 20 '17 at 15:14
  • 1
    Yep, I agree there isn't a definite need to do it this way, I merely wanted to point out that this is one debugging style... everyone seems to like doing it in their own way :) – Aditya M P Feb 22 '17 at 13:49
1

If you are developing in Visual studio and IE I like this more data-bind="somebinding:(function(){debugger; return bindvalue; })()" I like it more then echo function since it will go to the script with all the bindings rather the the eval file and you can just look at the $context $data(I use this in Chrome as well);

Filip Cordas
  • 2,531
  • 1
  • 12
  • 23
0

This works for me:

<div data-bind="text: function(){ debugger; }()"></div>
Robert J
  • 704
  • 10
  • 8
0

In case you're using KnockoutJS in a Magento2 project, you can use Magento's custom afterRender binding:

<div afterRender="function (target, viewModel) {
    console.log('Rendered element:', target);
    console.log('Associated view model:', viewModel);
    console.log(this === viewModel);
}"></div> 
bdbdbd
  • 416
  • 5
  • 13
0

This answer is based on the one from Dirk Boer. I added to his handler so that it also shows data when it is being updated:

ko.bindingHandlers.debug = 
{
    init: function(element, valueAccessor) 
    {
        console.log( 'Knockoutbinding (init):', element, ko.toJS(valueAccessor()));
    },
    update: function(element, valueAccessor)
    {
        console.log( 'Knockoutbinding (update):', element, ko.toJS(valueAccessor()));
    }
};

Then, all you have to do is to add a binding like this one in your HTML code:

<div data-bind="debug: $data">

and you will see the data in your console.log when it is initialized or updated.

Jean-François Beauchamp
  • 5,485
  • 8
  • 43
  • 77