在“beforeChange”订阅中获取可观察的新值

Posted

技术标签:

【中文标题】在“beforeChange”订阅中获取可观察的新值【英文标题】:Get new value of an observable in "beforeChange" subscription 【发布时间】:2017-05-13 12:51:11 【问题描述】:

我知道get the new value of an observable in a subscribe event 有解决方案(在实际更改之后),但我想知道是否可以在“beforeChange”订阅中访问 observable 的新值。

下面是current version of Knockout (3.4.1) 的 sn-p,分别使用旧值和新值引发 beforeChange 和 afterChange 订阅处理程序:

if (computedObservable.isDifferent(state.latestValue, newValue)) 
    if (!state.isSleeping) 
        computedObservable["notifySubscribers"](state.latestValue, "beforeChange");
    

    state.latestValue = newValue;
    if (DEBUG) computedObservable._latestValue = newValue;

    if (state.isSleeping) 
        computedObservable.updateVersion();
     else if (notifyChange) 
        computedObservable["notifySubscribers"](state.latestValue);
    

    changed = true;

显然 newValue 在“beforeChange”中可用,所以我想分叉是一种选择,但我不想这样做。

有没有其他解决方案来获得这个新值“beforeChange”?

【问题讨论】:

您能否详细说明为什么要在设置新值之前获取它? @MichaelBest 我试图阻止 FOUC。当我能够在实际设置之前读取新值时,我可以更新一些其他状态以防止不需要的视图中间渲染 您应该能够使用计算或订阅来更新可用于隐藏/显示/样式的第二个可观察对象。例如:jsfiddle.net/dmnqoc53 【参考方案1】:

编辑:我刚刚重读了您的问题,并认为您的意思可能是您想在 设置新值之前检查它?如果是这种情况,您可能想要执行this 之类的操作。


我通常创建两个订阅和一个额外的变量来跟踪旧的(您可能已经尝试过):

var myObs = ko.observable(1);

// Keep track of old
var myObsOld = myObs();
myObs.subscribe(function(oldVal) 
  myObsOld = oldVal;
, null, "beforeChange");

// Subscribe to new
myObs.subscribe(function(newVal) 
  console.log("myObs changed from", myObsOld, "to", newVal);
);

myObs(2);
myObs(3);
.as-console-wrapper  min-height: 100%; 
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>

现在,如果您不想在视图模型中添加额外的变量和订阅,可以将此逻辑封装在扩展器中:

ko.extenders.latestValue = function(target, option) 
  target.latestValue = target();
  
  target.subscribe(function(oldVal) 
    target.latestValue = oldVal;  
  , null, "beforeChange");
  
  return target;
;

var myObs2 = ko.observable("a").extend( latestValue: true );
myObs2.subscribe(function(newVal) 
  console.log("myObs2 changed from", myObs2.latestValue, "to", newVal);
);

myObs2("b");
myObs2("c");
.as-console-wrapper  min-height: 100%; 
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>

不是一个非常性感的方法,但我认为这比分叉回购更好:)

【讨论】:

感谢您的回答。实际上,我的问题是在实际更改之前检索新值,而不是在更改后检索旧值。包装 observable 会是什么样子?如何订阅?。【参考方案2】:

注意:此答案基于您对问题的评论,而不是原始问题。

当一个 observable 发生变化时,它会调用 observable 上的一个名为 notifySubscribers 的方法,该方法会执行实际通知,从而更新依赖于该 observable 的任何计算出的 observable、绑定等。如果您想在任何这些通知之前做某事,您有几个选择。

    正如@user3297291 所说,使用Ryan Niemeyer 的protectedObservable。

    在任何其他订阅之前立即订阅 observable。订阅者始终按顺序调用,因此您的代码将始终在其他任何内容之前运行。

    覆盖notifySubscribers以挂钩通知过程(记得调用上一个方法)。

    可能对于 Knockout 3.5.0,在 change 事件之前会出现 a spectate event。

【讨论】:

【参考方案3】:

这并不完全是对您请求的响应,但可能对稍微不同的用例有用。

如果您不坚持在更改传播到可观察对象之前调用订阅,并与 beforeChange 订阅一起触发,则可以创建自己的订阅,将新旧值一起推送到回调。

此订阅在更改发生后触发,它只是同时提供以前和当前的值。

/** 
 * extended version of knockout subscription provides to callback function new and previous value at once
 * function Callback(newValue, originalValue) ...
 * 
 * @param Function callback function called on observable change
 * @param Object context of the callback
 * @return Object instance of changed subscription
 */
ko.subscribable.fn.subscribeChanged = function (callback, context) 
    var savedValue = this.peek();
    return this.subscribe(function (latestValue) 
        var oldValue = savedValue;
        savedValue = latestValue;
        callback(latestValue, oldValue);
    , context);
;

【讨论】:

以上是关于在“beforeChange”订阅中获取可观察的新值的主要内容,如果未能解决你的问题,请参考以下文章

订阅可观察到的Angular 8组件中的问题

Angular 2:可观察订阅未正确读取数据[重复]

等待多个可观察请求完成使用 RXSwift

在 Angular 中链接可观察订阅的最佳方式?

merge和concat区别

可观察,在 ngOnDestroy 中取消订阅不起作用