Aurelia 通过@observable观察不可变对象

Aurelia 通过@observable观察不可变对象,aurelia,Aurelia,让我们假设我有一个全局状态类,它包含一个具有一些有用属性的TimeFrame类 export class StateContainer { public timeframe: TimeFrame; public setTimeframe(startTime: Date, endTime: Date) { // treat this.timeframe as immutable this.timeframe = { startTime: sta

让我们假设我有一个全局状态类,它包含一个具有一些有用属性的TimeFrame类

export class StateContainer {
    public timeframe: TimeFrame;

    public setTimeframe(startTime: Date, endTime: Date) {
         // treat this.timeframe as immutable
         this.timeframe = { startTime: startTime, endTime: endTime };
    }
}

export class TimeFrame {
    public readonly startTime: Date;
    public readonly endTime: Date;
}
然后,我需要在其他地方使用此状态,因此我通过DI执行此操作,然后使用
bindingEngine.propertyObserver
获取
时间范围
对象的更改

但是,如果可能的话,我希望能够做以下类似的事情:

@autoinject
export class Display {
    @observable
    timeFrame: TimeFrame = this.state.timeframe;      

    constructor(private state: StateContainer) {

    }

    timeFrameChanged(newValue: TimeFrame, oldValue: TimeFrame) {
         ...
         // everytime this.state.timeFrame is changed via setTimeFrame()
         // this change handler should fire, and so should any
         // bindings from this component for this.timeFrame
    }
}

但是,当我执行前面的操作时,我只会在初始创建时收到
timeframe更改(…)
通知,而不是每次调用
setTimeFrame()
。我做错了什么还是不可能?

是的,你做错了什么,我想你误解了
可观察的
装饰器和相应的
时间框架更改
方法是如何使用的

让我们一点一点地看。请看以下几行:

@observable
timeFrame: TimeFrame = this.state.timeframe;

timeFrameChanged(newValue: TimeFrame, oldValue: TimeFrame) { ... }
observable
装饰器告诉Aurelia,只要应用它的属性发生变化,就执行相应的方法。除非另有配置,否则相应的方法是
nameOfInvolvedProperty+“Changed”
(在本例中,正如您所做的那样,
timeFrameChanged

但是,您从未实际更改该属性的值!如果您实际更改了该属性,它将起作用:

<button click.delegate="changeProp()">
  Change prop
</button>
现在您将看到处理程序正确地触发

但在您的代码中,此属性仅分配一次,如下所示:

timeFrame: TimeFrame = this.state.timeframe;

记住,这只是取消引用。换句话说,此代码告诉应用程序获取
this.state.timeframe
,将其瞬时值存储在该变量中,然后忘记
this.state.timeframe
。因此,无论何时更新
this.state.timeframe
,它都不会得到更新

如中所述,您可以在某种程度上配置
可观察的
装饰器,但据我所知,没有办法将其配置为可以观察嵌套属性(或者可能只是我不知道如何操作)

即使有可能,我认为处理这种情况的一个更干净的方法是使用一个。这使您能够采用订阅机制—只要您感兴趣的值发生更改,您就发布一个事件,任何对该更改感兴趣的组件都可以订阅它并相应地更新自身

一个简单的实现:

import { Router, RouterConfiguration } from 'aurelia-router';
import { autoinject } from 'aurelia-framework';
import { observable } from 'aurelia-binding';
import { StateContainer } from './state-container';
import { EventAggregator, Subscription } from 'aurelia-event-aggregator';

@autoinject
export class App {

  subscription: Subscription = null;

  constructor(private state: StateContainer, private eventAggregator: EventAggregator) {

  }

  activate() {
    this.subscription = this.eventAggregator.subscribe('state:timeframe', (e) => {
      console.log('Timeframe changed!', e);
    });
  }

  deactivate() {
    // Make sure to dispose of the subscription to avoid memory leaks.
    this.subscription.dispose();
  }

  change() {
    this.state.setTimeframe(new Date(), new Date());
  }
}
StateContainer

import { TimeFrame } from "./time-frame";
import { autoinject } from "aurelia-framework";
import { EventAggregator } from "aurelia-event-aggregator";

@autoinject
export class StateContainer {
  public timeframe: TimeFrame = null;

  constructor(private eventAggregator: EventAggregator) {

  }

  public setTimeframe(startTime: Date, endTime: Date) {
    // treat this.timeframe as immutable
    this.timeframe = { startTime: startTime, endTime: endTime };

    this.eventAggregator.publish('state:timeframe', this.timeframe);
  }
}

希望这会有所帮助。

“记住,这只是取消引用。换句话说,此代码告诉应用程序获取this.state.timeframe的值,将其瞬时值存储在该变量中,并忘记this.state.timeframe。因此,每当this.state.timeframe更新时,它不会得到更新。”这帮助我理解了很多,谢谢。我想知道observable装饰器是否有一个配置属性,我可以在其中执行类似的操作:
@observable(this.state.timeFrame)timeFrame:timeFrame但是,没关系。编辑:格式化是的,那确实很好。而且,我最终使用了一个修改过的通量存储来管理状态,而不是核事件聚合器。基本原理相同,范围较小。(尽管如此,我也可以创建一个范围更小的事件聚合器)
import { TimeFrame } from "./time-frame";
import { autoinject } from "aurelia-framework";
import { EventAggregator } from "aurelia-event-aggregator";

@autoinject
export class StateContainer {
  public timeframe: TimeFrame = null;

  constructor(private eventAggregator: EventAggregator) {

  }

  public setTimeframe(startTime: Date, endTime: Date) {
    // treat this.timeframe as immutable
    this.timeframe = { startTime: startTime, endTime: endTime };

    this.eventAggregator.publish('state:timeframe', this.timeframe);
  }
}