在 Knockout.js 订阅的帮助下为一组对象设置动画?

Animating a group of objects with the help of a Knockout.js subscription?

我花了过去 6 个小时试图解决这个问题,但我找不到关于 Knockout.js 中订阅如何工作的文档,现在它已从 "mildly annoying" 类别中移出至 "downright frustrating"。

我想要完成什么?

我需要为 DOM 的一部分设置动画,以便它根据需要上下滑动。这是以这种方式动画化的 DOM 的一般结构:

<section id="add" class="manageOption">
</section>
<section id="replace" class="manageOption">
</section>
<section id="remove" class="manageOption">
</section>

每个 .manageOption 具有相同的大小和尺寸,看起来非常相似。我的 ViewModel 中有一个名为 self.managementState 的可观察对象,它最初设置为 false。单击不相关元素上的绑定会将此 observable 设置为 false(如果它们均未显示)或添加/替换/删除,具体取决于应显示的元素。一次只能显示一个 .manageOption

但是,仅当 self.managementState false 到 [=36] 时,才会出现 slideUp 或 slideDown 效果=] false。因此,我不仅需要知道 self.managementState 的当前值,还需要知道之前的值。如果值从 add 更改为 remove,我不需要为元素设置动画,更改会立即发生。

我试过的

为了解决这个问题,我一直想构建一个名为 slideSwitcher 的自定义绑定,它使用我描述的上述逻辑进行操作,而且我还发现这部分代码应该会获取先前的值一个可观察的:

function subscribeToPreviousValue(observable, fn) {
    observable.subscribe(fn, this, 'beforeChange');
}

但我无法在自定义绑定中的 update 函数中很好地发挥它的作用,它总是 returns 错误的结果(例如,false 已更改为 false)。

ko.bindingHandlers.slideSwitcher = {
    update: function (element, valueAccessor) {
        var observable = valueAccessor();
        var currentVal = ko.utils.unwrapObservable(valueAccessor());

        subscribeToPreviousValue(observable, function (previous) {
            console.log('value changed from', previous, 'to', currentVal);
        });
    }
}

从根本上说,这是因为我根本不理解 'subscription' 的概念,而且我自己编写绑定的经验也很少。我如何使用上面的订阅函数来跟踪一个可观察对象的先前值,我应该如何构建我的绑定来实现我需要的?

第一个问题是您在更新回调中设置了订阅。当某些依赖项发生更改时,将调用更新函数。所以订阅 beforechange 事件为时已晚。并且您还可以在每次值更改后设置一个新订阅。您必须订阅 init 函数中的值。

我喜欢 subscribeChanged 函数(Get previous value of an observable in subscribe of same observable - 第二个答案),它很好而且清晰...

ko.subscribable.fn.subscribeChanged = function (callback) {
    var oldValue;
    this.subscribe(function (_oldValue) {
        oldValue = _oldValue;
    }, this, 'beforeChange');

    this.subscribe(function (newValue) {
        callback(newValue, oldValue);
    });
};

并且可以这样写绑定

ko.bindingHandlers.slideSwitcher = {
    init: function (element, valueAccessor) {
        var observable = valueAccessor();
        observable.subscribeChanged(function (newValue, oldValue) {
            console.log('value changed from', oldValue, 'to', newValue);
        });          
    }
}