【问题标题】:Knockout computed vs. subscription, timing issues淘汰赛计算与订阅,时间问题
【发布时间】:2013-12-04 02:11:42
【问题描述】:

刚刚发现,在 KnockoutJS 中,订阅函数是在依赖可计算之前评估的,并且需要有人可以提交,因为我在文档或论坛中找不到任何关于 Knockouts 时间的信息。

这意味着:如果我有这样的模型......

var itemModel = function (i) {
    var self = this;

    self.Id = ko.observable(i.Id);
    self.Title = ko.observable(i.Title);
    self.State = ko.observable(i.State);

};

var appModel = function () {
   var self = this;

   self.Items = ko.observableArray() // <-- some code initializes an Array of itemModels here
   self.indexOfSelectedItem = ko.observable();

   self.selectedItem = ko.computed(function () {
       if (self.indexOfSelectedItem() === undefined) {
            return null;
       }
       return self.Items()[self.indexOfSelectedItem()];
   });
};

我想用一个可观察的索引字段来跟踪选定的数组项,我像这样订阅这个索引字段...

appModel.indexOfSelectedItem.subscribe(function () {
    // Do something with appModel.selectedItem()
    alert(ko.toJSON(appModel.selectedItem()));
}

...在使用新的索引值重新评估计算之前评估订阅函数,因此我将获得对应于最后选择的索引而不是实际选择的索引的 selectedItem()。

两个问题:

  • 是这样吗?
  • 那么,如果一个简单的函数每次调用它时都能让我获得当前选定的项目,而 ko.computed 会在一切都已经完成并且我不再需要它的任何时候进行评估,那我为什么要使用 ko.computed() ?

【问题讨论】:

  • 为什么不订阅 selectedItem?所以appModel.selectedItem.subscribe(function () { 在这种情况下,当selectedItem 发生变化时,您的函数将被调用。顺便说一句,您的计算中有一个错误:return self.Items[self.indexOfSelectedItem()]; 应该是 return self.Items()[self.indexOfSelectedItem()]; 注意额外的 ()
  • 因为不仅有一个计算依赖于索引,还有更多。我需要一个处理程序,我可以在其中更新 UI 中的很多内容,并且所有这些计算字段也已经更新。我现在应该订阅哪一个?我认为计算对象就像一个 getter 函数,当我调用它时会对其进行评估。事实上,有一段时间,这些值绑定到旧值,所以我认为最好使用简单的 getter 函数,不是吗?谢谢你对额外的()是正确的......

标签: javascript knockout.js


【解决方案1】:

默认情况下,Knockout 中的所有计算都以急切的方式进行评估,而不是延迟评估(即,当您第一次访问它们时,不是)。

一旦一个依赖发生变化,所有订阅都会收到通知,所有连接的计算都会重新评估。您可以通过在计算的 observable 中指定 the deferEvaluation option 来将该行为更改为“惰性”,但您不能对订阅执行此操作。

但是,我认为没有必要依赖于所选项目的索引。事实上,这甚至是糟糕的设计,因为您真正关注的不是索引的数值,而是它所代表的项目。

您可以通过创建一个可写的计算 observable 来反转依赖关系,它为您提供当前所选项目的索引(用于显示目的)并允许更改它(为方便起见)。

function AppModel() {
    var self = this;

    self.Items = ko.observableArray();
    self.selectedItem = ko.observable();

    self.indexOfSelectedItem = ko.computed({
        read: function () {
            var i,
                allItems = self.Items(),
                selectedItem = self.selectedItem();

            for (i = 0; i < allItems.length; i++) {
                if (allItems[i] === selectedItem) {
                    return i;
                }
            }
            return -1;
        },
        write: function (i) {
            var allItems = self.Items();

            self.selectedItem(allItems[i]);
        }
    });
}

Knockout 倾向于存储/处理实际值,而不仅仅是值的索引,因此对视图进行必要的更改可能并不难。只需将以前写给indexOfSelectedItem 的所有内容现在直接写给selectedItem。 selectedItem 的依赖将继续正常工作。

在设计良好的 Knockout 应用程序中,您很少需要处理数组项的索引。一旦一切正常,我建议删除计算的 write 部分。

见:http://jsfiddle.net/4hLLn/

【讨论】:

  • 谢谢,这确实是一个非常有用的建议(实际上我对如何设计一个淘汰赛应用程序了解不多)。我会这样做。我还需要索引是可写的,因为我将项目绑定到一个 jquery ui 手风琴,其中手风琴面板对应于数组索引。因此,如果我单击手风琴面板来选择项目,除了将 appModel.indexOfSelectedItem() 设置为手风琴中的面板索引之外,我还没有找到更好的选择方法(面板不知道 KO 模型类) .有没有更好的方法来做到这一点?
  • 我认为有。我会尝试为此想出一个小提琴,等等。
  • 关键点是将视图、模型和绑定相互分离。最简洁的方法是使手风琴成为绑定声明的属性。例如,您可以声明意图“此元素应显示为手风琴,当前活动面板应与所选项目相关联”。由于 Knockout 不了解手风琴,因此您可以为此创建自定义绑定,请看这里:jsfiddle.net/4hLLn/1。 (免责声明:这是一个示例。其他人已经为 jQuery UI 小部件创建了更完整的自定义绑定,请查看。)
  • 啊,好吧。由于某种原因,小提琴拒绝在 IE 中运行。不过,似乎是特定于 jsFiddle 的。 Firefox 和 Chrome 运行良好。
  • 这里有一个小扩展:jsfiddle.net/4hLLn/2 它允许您动态绑定 Accorion 选项(例如,'disabled')到您的视图模型。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-09-20
  • 2016-02-29
  • 2018-12-27
  • 1970-01-01
  • 2012-06-13
相关资源
最近更新 更多