服务仅适用于最新添加的动态创建组件
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$
流以多播点击次数。
我正在 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$
流以多播点击次数。