如何调试 KnockoutJS 的模板绑定错误?

204

我在KnockoutJS模板中调试问题时一直感到困扰。

比如,我想绑定一个名为"items"的属性,但在模板中我打错了字,绑定到了(不存在的)属性"item"。

使用Chrome调试器只告诉我:

"item"未定义。

是否有工具、技巧或编码风格可以帮助我获得有关绑定问题更多信息?

14个回答

360

当特定范围内可用数据存在问题时,我经常使用替换模板/部分的方式来解决,例如:

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

或者,如果您想要一个稍微更易读的版本:

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

这将输出绑定在该作用域的数据,并确保您正确嵌套事物。

更新:自KO 2.1起,您可以简化为:

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

现在这些参数被传递到JSON.stringify函数。


3
我需要考虑更多的调试技巧,也许会写一篇博客文章。另一个我想到的是手动使用订阅观察值或计算观察值来监视值的变化。例如,如果 name 是一个可观察的对象,可以执行 name.subscribe(function(newValue) { console.log("name", newValue); }); - RP Niemeyer
1
可能是因为这个答案比较旧,但为什么不使用console.log并利用调试器的全部功能来查看对象属性呢?请参见:https://dev59.com/C2ox5IYBdhLWcg3wWjHQ#16242988。 - Dirk Boer
1
@DirkBoer - 使用console.log也是一个很好的方法。很多时候,我想在我的元素旁边看到数据,比如在foreach场景中,我发现在相关的渲染标记中更容易看到它,而不是在控制台中查找。这取决于情况。这里有一些我的想法:http://www.knockmeout.net/2013/06/knockout-debugging-strategies-plugin.html。此外,您可能还想在绑定中记录“干净”的版本,例如`console.log(ko.toJS(valueAccessor())`。 - RP Niemeyer
1
@RuneJeppesen - 我不确定您正在序列化哪种类型的数据,但是像这样的东西可能会有所帮助:http://www.knockmeout.net/2011/04/controlling-how-object-is-converted-to.html - RP Niemeyer
你甚至可以使用 data-bind="text: console.log($data)" 来浏览绑定的对象,从而实现此操作。 - Grirg
显示剩余3条评论

61

如果您在进行开发时使用 Chrome 浏览器,有一个非常好的扩展(我与之无关)称为Knockoutjs上下文调试,它可以直接在开发者工具的元素面板中显示绑定上下文。


3
我希望Firefox或Firebug有这个功能。有人知道这样的东西吗? - Patrick Szalapski
似乎已经停止支持。如果使用复杂的数据绑定结构,会导致Chrome崩溃。大约一年来,它对我的任何项目都没有起作用。 - Arctic
很抱歉听到这个消息,不过我早已从 KO 转向了 Ember。 - neverfox
1
对我来说,它(大多数情况下)运行良好,我有一些非常复杂的结构。我没有尝试过,但在扩展选项中建议:“如果您遇到崩溃,您可能有一个不可序列化的视图模型。您可以关闭序列化。”消息下面有一个复选框用于禁用此功能。 - Grinn
非常有用,立即使用,谢谢。 - Andrew

42

在你的 JavaScript 库文件中的某处定义一个 bindingHandler。

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

那么只需像这样使用:

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

优点

  • 完全利用Chrome调试器的功能,例如“在元素面板中显示”(Reveal in Elements Panel)
  • 您无需为了调试而向DOM添加自定义元素

输入图像说明


33

我发现另一个可能有用的解决方法。我正在调试某些绑定,尝试使用Ryan的示例。结果我收到了JSON找到循环引用的错误。

<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>

但是,使用这种方法,我将data-bind的值替换为以下内容:

  <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>

现在,如果我在Chrome调试窗口打开的情况下点击PRE元素,我会得到一个很好的填充作用域变量窗口。

找到了一种更好的方法:

<pre data-bind="text: ko.computed(function() { debugger; })"></pre>

非常有用。在使用<pre data-bind="text: ko.toJSON($data, null, 2)"></pre>时,遇到了knockout循环和Razor标记问题。使用<pre... debugger>是一个完美的解决方法。由于某种原因,RAZOR输入(如@Html.CheckBox)会破坏ko.toJSON。 - Arctic

22

逐步指南

  1. 在本指南中,我们将使用官方 KnockoutJS 示例之一
  2. 假设您想查看第二个联系人(Sensei Miyagi)背后的数据。
  3. 右键单击第二个联系人的第一个输入框(带有文本“Sensei”)。
  4. 选择“检查元素”。Chrome 开发者工具栏将打开。
  5. 打开 JavaScript 控制台窗口。您可以通过单击 Chrome 开发者工具栏底部左侧的 >= 图标或打开 Chrome 开发者工具栏中的“控制台”选项卡或按下 Ctrl+Shift+J 来访问控制台。
  6. 键入以下命令并按 Enter:ko.dataFor($0)
  7. 现在,您应该可以看到绑定到第二行的数据。您可以通过按下对象树左侧的小三角形来展开数据以导航对象树。
  8. 键入以下命令并按 Enter:ko.contextFor($0)
  9. 现在,您应该可以看到一个包含整个 Knockout 上下文(包括根和所有父级)的复杂对象。当您编写复杂的绑定表达式并希望尝试不同的结构时,这非常有用。

Example output when following above guide

这是什么黑魔法?

这个技巧是Chrome的$0-$4功能KnockoutJS的实用方法的组合。简而言之,Chrome会记住你在Chrome开发者工具栏中选择的元素,并将这些元素暴露在别名$0$1$2$3$4下。因此,当您右键单击浏览器中的元素并选择“检查元素”时,该元素自动成为别名$0下可用的元素。您可以将此技巧与KnockoutJS、AngularJS、jQuery或任何其他JavaScript框架一起使用。

这个技巧的另一面是KnockoutJS的实用方法ko.dataFor和ko.contextFor:

  • ko.dataFor(element) - 返回可用于绑定元素的数据
  • ko.contextFor(element) - 返回可用于DOM元素的整个绑定上下文。
记住,Chrome的JavaScript控制台是一个完全功能的JavaScript运行环境。这意味着您不仅限于查看变量。您可以存储ko.contextFor的输出并直接从控制台操作视图模型。尝试var root = ko.contextFor($0).$root; root.addContact();,看看会发生什么:-)
调试愉快!

7

看看我使用的一个非常简单的东西:

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

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

在HTML中,假设你有以下内容:

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

只需将其替换为

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

更高级的内容:
function echo(vars, member) { console.log(vars); debugger; return vars[0][member]; }

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

希望您能享受这篇文章 :)

更新

另一个让人烦恼的事情是当您试图绑定到未定义的值时。想象一下上面的例子中,数据对象只是 {} 而不是 { value: 'some text' }。在这种情况下,您将会遇到麻烦,但通过以下微调,您将会没问题:

<div data-bind="text: $data['value']"></div> 

5

4

最简单的方法是将数据放到控制台中以查看传递给绑定的数据:

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

Knockout将评估文本绑定的值(实际上可以使用任何绑定),并将$data刷新到控制台浏览器面板。

3

其他答案都很好,我只是想分享一下我的做法:

在你的视图中(假设你已经绑定了一个ViewModel):

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

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

执行此操作将暂停调试器中的代码,elementvalueAccessor() 将包含有价值的信息。


不需要自定义绑定。请查看http://stackoverflow.com/documentation/knockout.js/5066/debugging-a-knockout-js-application/27325/printing-a-binding-context-from-markup#t=201702201513545237604 - Adam Wolski
1
是的,我同意没有必要一定要这样做,我只是想指出这是一种调试风格...每个人似乎都喜欢用自己的方式来做 :) - Aditya M P

1
如果您在Visual Studio和IE中进行开发,我更喜欢这种方式:data-bind="somebinding:(function(){debugger; return bindvalue; })()"。我比较喜欢它而不是echo函数,因为它会将所有绑定传递到脚本中,而不是eval文件,您可以查看$context $data(我在Chrome中也使用它)。

我敢打赌这与Visual Studio或IE无关。 - Serhiy
@Serhiy 在Chrome中也是一样的,但我认为在Chrome中你可以访问文件,但我不认为你可以在VS中访问文件。 - Filip Cordas

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接