服务仅适用于最新添加的动态创建组件

Service only works in latest added dynamically create component

我正在 Angular 中构建一个类似黄金布局的面板对接系统。我使用动态创建的组件将面板添加到页面,如下所示:

@ViewChild('dynamicInsert', {read: ViewContainerRef }) dynamicInsert: ViewContainerRef;

/**
* This is the onChanges for the dock component
*/
ngOnChanges() {
    const componentFactory = this.componentFactoryResolver.resolveComponentFactory(this.panelType);
    this.dynamicInsert.clear();
    this.panelRef = this.dynamicInsert.createComponent(componentFactory);
    this.panelRef.location.nativeElement.setAttribute('dock-tree-id', this.dockTreeId);
    this.panelRef.location.nativeElement.setAttribute('movable', this.movable);

    this.initializePanel(this.panelRef.instance);
}

dockTree 是一个包含当前打开的不同面板的对象。这样我应该能够很容易地实现将当前布局保存到本地存储。

目前我的页面有多个 'docks' 像这样:

<div #dock
     class="dock">
  <ng-template #dynamicInsert></ng-template>
</div>

为了在这些面板之间进行通信,我使用了一项服务。例如我创建了 clickService。一个面板有一个调用 click() 的按钮,另一个面板订阅了 getClicks():

@Injectable()
export class ClickService {

  private clicks = 0;
  private observer;

  public getClicks(): Observable<number> {
    return new Observable<number>((observer: Observer<number>) => {
      this.observer = observer;
      observer.next(this.clicks);
    });
  }

  public click() {
    this.clicks++;
    this.observer.next(this.clicks);
  }
}

现在当我点击按钮时,其他订阅我服务的面板应该全部更新,但只有最后添加的面板更新:

当我动态添加一个新面板时,它添加了一个新元素并附加了面板,新面板将被订阅,但前一个面板会中断。将面板拖放到新位置时触发 ngOnChanges,面板将具有正确的值,但只有最后一个面板会听到点击。

为什么会这样?

问题在于,无论何时调用 getClicks,对上一个点击观察者的引用都会被最新的观察者所取代。所以基本上你最多只有一 (1) 个观察者,与创建的面板数量无关。

private observer;

  public getClicks(): Observable<number> {
    return new Observable<number>((observer: Observer<number>) => {
      this.observer = observer; // replaces the previous observer with the newest
      observer.next(this.clicks); // pushes the current counter to the newest observer
    });

   public click() {
     this.clicks++;
     this.observer.next(this.clicks); // only the latest observer gets the new state
   }
  }

我不知道您为什么选择创建一个 observable 来多播来自唯一来源的点击次数,但这可以通过使用 Subject:

import { BehaviorSubject } from 'rxjs/BehaviorSubject';

@Injectable()
export class ClickService {
  private readonly _clicks$ = new BehaviorSubject<number>(0);

  readonly clicks$: Observable<number> = this._clicks$.asObservable();

  public click() {
    this._clicks$.next(this._clicks$.value + 1);
    // some would argue that using a sync. value access in a subject is bs
  }
}

现在您应该订阅 clicks$ 流以多播点击次数。