2

我希望有人可以提供一些指导。

这是关于以最佳方式利用 Aurelia Observers。

这个例子是一个虚构的例子来说明感知到的问题。

假设我有三个数字属性:小时、分钟、秒,也就是 H、M、S,我希望构建一个监视 H、M、S 的对象,并且每次这三个更改中的任何一个时,它都会生成一个属性 T,它是字符串这看起来像一个持续时间,例如。"hh:mm:ss" 使用 H、M、S 属性和适当的零填充等。

相反,属性 T 绑定到用户界面中,因此如果用户修改 T(例如通过输入字段),我需要将 T 的各个部分分解为它们的 H、M、S 组件并将各个值推回进入 H、M、S 属性。

任何人都不应忘记,这种行为在“toView”和“fromView”行为方面与 Aurelia 值转换器的行为非常相似,除了我们在一侧处理 3 个属性,在另一侧处理 1 个属性.

在实现方面,我可以使用 BindingEngine 或 ObserverLocator 在 H、M、S 和 T 属性上创建观察者,并订阅这些更改通知。这一点很容易。

我的问题最终是这样的:

当 H 发生变化时,将触发对 T 的重新计算,因此我将向 T 写入一个新值。

更改 T 将触发 T 的更改通知,然后其处理程序将分解 T 的值并将新值写回 H、M、S。

写回 H、M、S 会触发另一个更改通知以生成新的 T,依此类推。

似乎不言自明的是——至少——当我的“转换器”对象写入 H、M、S、T 属性时,它应该以某种方式准备好自己以忽略它期望的更改通知即将到来的结果。

但是,说和做是两件不同的事情。

我的问题是——真的有必要吗?如果需要,我该如何以最简单的方式进行操作。这样做的障碍如下:

必须对值进行真正的更改才能生成更改通知,因此您需要提前知道您是否“期望”收到通知。更困难的问题是更改通知是通过 Aurelia “合成”微任务队列发出的,因此您非常不知道何时会收到该微任务调用。

那么,有没有很好的解决这个问题的办法呢?有没有人真的担心这一点,或者他们只是依靠第 1 点产生自限性的结果?也就是说,可能会有几个周期的更改通知,但该过程最终会默认吗?

4

1 回答 1

2

实现 N 路绑定(在本例中为 4 路)的最简单方法是使用更改处理程序和“忽略”标志来防止无限递归。

下面的概念与 Aurelia 框架内部解决其中一些问题的方式非常相似——它是健壮的、高性能的,并且尽可能“自然”。

@autoinject()
export class MyViewModel {
    @bindable({ changeHandler: "hmsChanged" })
    public h: string;

    @bindable({ changeHandler: "hmsChanged" })
    public m: string;

    @bindable({ changeHandler: "hmsChanged" })
    public s: string;

    @bindable()
    public time: string;

    private ignoreHMSChanged: boolean;
    private ignoreTimeChanged: boolean;

    constructor(private tq: TaskQueue) { }

    public hmsChanged(): void {
        if (this.ignoreHMSChanged) return;
        this.ignoreTimeChanged = true;
        this.time = `${this.h}:${this.m}:${this.s}`;
        this.tq.queueMicroTask(() => this.ignoreTimeChanged = false);
    }

    public timeChanged(): void {
        if (this.ignoreTimeChanged) return;
        this.ignoreHMSChanged = true;
        const hmsParts = this.time.split(":");
        this.h = hmsParts[0];
        this.m = hmsParts[1];
        this.s = hmsParts[2];
        this.tq.queueMicroTask(() => this.ignoreHMSChanged = false);
    }
}

如果您需要一个通用解决方案,您可以在具有不同类型 N 路绑定的多个 ViewModel 中重用,您可以使用 BindingBehavior(甚至可能是 2 个 ValueConverters 的组合)来实现。

但是需要在其中实现的逻辑在概念上与我上面的示例相似。

事实上,如果框架中有针对该问题的预制解决方案(例如,@bindable()装饰器的设置),那么该解决方案的内部逻辑也将是相似的。您将始终需要这些标志,并通过微任务延迟它们的重置。

避免使用微任务的唯一方法是更改​​框架的某些内部结构,以便可以传递上下文,它可以告诉调度程序更改通知“此更改来自更改处理程序,因此不要触发该更改再次处理”

于 2018-05-11T10:47:52.460 回答