0

我在页面上有一个元素,如下所示:

<span data-bind="text: FormattedCountOfPeople"></span>

计算它的函数我首先是这样写的:

self.FormattedCountOfPeople = ko.computed(function () {
    if(!self.PeopleCountFormatString)                 //a string like "{0} people in the conference", but set by a service call so starts out undefined
      return "not set yet";

    let sizes = self.Params().PermittedCounts();      //an array of discrete permitted sizes e.g. [2, 10, 30]
    let size = self.Params().ChosenCount();           //value set by a jquery slider, could be any double, even 5.7435 - to achieve smooth dragging of the slider it has a small step but snaps to a permitted count
    let n = nearest(size, sizes);                     //a "round to nearest" helper function that given args of e.g. (5.7345, [2, 10, 30]) will pick 2 because 5.7345 is nearer to 2 than 10
    let s = self.PeopleCountFormatString.csFormat(n); //csformat is a helper function that performs C# style string Formatting e.g. "{0} people".csFormat(2) -> "2 people"
    return s;
});

我在圈子里转了几个小时想知道为什么页面上的文本只是卡在“尚未设置”,不管滑块设置是什么,但是<span data-bind="text: Params().ChosenCount"></span>作为测试添加的另一个元素正在完美更新,而其他地方的另一个滑块是使用类似的逻辑设置小时和分钟的持续时间就可以了:

//if the user picks 61.2345, it rounds to 60, then returns "1 hour". Picking 74.11 rounds to 75 then returns "1 hour, 15 min"
self.FormattedDurationText = ko.computed(function () {
    var duration = self.Params().ChosenDuration();  
    duration = Math.round(duration / 15) * 15;

    if (Math.floor(duration / 60) > 0)
        var hours = self.DurationTextHours.csFormat(Math.floor(duration / 60));
    if (duration % 60 > 0)
        var minutes = self.DurationTextMinutes.csFormat(duration % 60);
    if (minutes && hours)
        return self.DurationTextLayout.csFormat(hours, minutes)
    if (hours && !minutes)
        return hours;
    if (!hours && minutes)
        return minutes;
    return "";
});

在各个地方添加一些控制台日志记录很明显,在第一次调用FormattedCountOfPeople返回“尚未设置”之后,FormattedCountOfPeople拖动人员计数滑块时再也没有调用过。直接绑定到原始数​​据的另一个跨度Params().ChosenCount将不断更新,因此值变化正常。同样,绑定到的滑块Params().ChosenDuration正在更新值,并且FormattedDuration()每次值更改时都会被调用,并提供一个新的格式化字符串以进入跨度

最后,使事情正常进行的代码更改似乎非常无关紧要。我删除了初始if并将其交换为内联条件:

self.FormattedCountOfPeople = ko.computed(function () {
    let sizes = self.Params().PermittedCounts();
    let size = self.Params().ChosenCount();
    let n = nearest(size, sizes); 
    let s = (self.PeopleCountFormatString ? self.PeopleCountFormatString: "not set yet").csFormat(n); 
    return s;

为什么做出这个改变突然意味着FormattedCountOfPeople每次值改变时都开始调用淘汰赛?我能看到的唯一真正不同的是,FormattedDurationText它的结构使得它总是Params().ChosenDuration()在决定值不好并返回之前调用,""而第一个版本的FormattedCountOfPeople行为偶尔决定不调用任何东西,而第二个版本总是调用。诸如“重置标志”之类的调用是否有副作用Params().Xxx(),如果标志从未重置,那么即使值发生变化,淘汰赛也不会再次调用该函数?

4

1 回答 1

1

当您在计算中引用一个可观察对象时,KO 将在内部订阅该可观察对象,因此每次可观察对象更改时都会重新评估计算对象。

当您在计算开始时执行此操作时:

if(!self.PeopleCountFormatString)
  return "not set yet";

并且self.PeopleCountFormatString 不是可观察的(它似乎不是),由于 return 语句,您的计算不会进一步评估,订阅ChosenCount永远不会发生,并且因为PeopleCountFormatString它本身也不是可观察的,所以计算的不是稍后重新评估何时PeopleCountFormatString确实具有价值。因此,它的价值将永远保持“尚未设定”。

更新后的计算有效,因为您立即引用其他可观察值,因此在内部 KO 将订阅那些并在可观察值更改时重新评估计算值。

于 2020-11-25T12:21:08.197 回答