Binding 使用Aurelia属性观察器忽略您自己的更改的最佳方法

Binding 使用Aurelia属性观察器忽略您自己的更改的最佳方法,binding,aurelia,property-observer,Binding,Aurelia,Property Observer,我希望有人能提供一些指导 它是关于以最好的方式利用奥雷利亚观察者 该示例是一个虚构的示例,用于说明感知到的问题 假设我有三个数字属性:hours,mins,secs,又名H,M,s,我希望构建一个监视H,M,s的对象,每当这三个属性中的任何一个发生变化时,它都会生成一个属性T,它是一个看起来像持续时间的字符串,例如“hh:mm:ss”,使用H,M,s属性,并使用适当的零填充等 相反,属性T在用户界面中绑定到,因此如果用户修改了T(例如,通过输入字段),我需要将T的各个部分分解为它们的H、M、S组

我希望有人能提供一些指导

它是关于以最好的方式利用奥雷利亚观察者

该示例是一个虚构的示例,用于说明感知到的问题

假设我有三个数字属性:hours,mins,secs,又名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 value converter的行为非常相似,这一点不应让任何人感到迷惑,只是我们处理的是一侧的3个属性,另一侧的1个属性

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

我的问题最终是:

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

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

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

似乎不言而喻的是——至少——当我的“转换器”对象写入H、M、S、T属性时,它应该以某种方式准备好忽略它预期将作为结果出现的更改通知

然而,说和做的容易是两码事

我的问题是,这真的有必要吗?如果这是可取的,我该如何以最简单的方式去做呢。这样做的障碍如下:

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


那么,这个问题有好的解决方案吗?是否有人真的担心这一点,或者他们仅仅依靠第1点产生自我限制的结果?也就是说,可能会有几个周期的更改通知,但流程最终会默认?

实现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向绑定的多个ViewModels之间重用,那么可以通过BindingBehavior(甚至可能是2个ValueConverter的组合)来实现

但是,在这些应用程序中需要实现的逻辑在概念上类似于我上面的示例

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


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

谢谢Fred;我想问题是奥雷莉亚是否有更好的方法来处理这件事,但我很高兴这是一种模式。