Angular 打开对话框并 return 可观察到操作结果

Angular open a dialog and return observable for action result

我想通过代码显示一个对话框(不是 MatDialog,而是一个自定义的)然后 return 一个 Observable 将触发一个结果给调用者。

像这样:

    @ViewChild(PopupComponent)
      myDialog: PopupComponent;


public show(){
   this.myDialog.show().subscribe(v => {
       if(v.OkClicked){
          console.log('ok clicked!');
       }
   });
}


export interface DialogResult{
    OkClicked: boolean;
}

@Component({
  selector: 'f-popup',
  outputs: [ "onClose" ],
  templateUrl: './popup.component.html',
  styleUrls: ['./popup.component.css']
})

private _result : ReplaySubject<DialogResult>;
private _obsResult: Observable<DialogResult>;

public show(): Observable<DialogResult>(){
    // show the dialog
    if(!this._result){
        this._result = new ReplaySubject<DialogResult>(1);
    }
    return this._obsResult.asObservable();
}

public onOkClick(){
   let result: DialogResult = {OkClicked: true};
   this._obsResult.next(result);
}

public onClosed(){
    this._obsResult.complete();
    this._obsResult = null;
}

ngOnDestroy() {
    if(this._obsResult) {
      this._obsResult.complete();
      this._obsResult = null;
   }
}

对话框在关闭时不会被销毁,它是显示和隐藏的另一个组件中的一个组件。 我的问题是,在对话的每个节目中重新创建主题是否是一种好方法?这种方式是否存在内存泄漏?

我建议反过来做。

您可以在打开对话框时将带有可观察对象的 data 对象传递给您的对话框。当在 PopupComponent 中确认操作(单击确定)时,您订阅了传递的 action$ 可观察对象。您可以选择传入一个 cancel$ observable 以在用户单击取消或关闭时执行某些操作。

interface PopupData<T> { 
  action$: Observable<T> // Action to which you subscribe on OK
  cancel$?: Observable<void> // Optional observable for handling cancel
}

@Component({
  selector: 'f-popup',
  templateUrl: './popup.component.html',
  styleUrls: ['./popup.component.css']
})

public data: PopupData<any>;

public show(data: PopupData<T>): Observable<T>(){
  this.data = data;
}

public onOkClick(){
  this.data.action$.subscribe();
}

public onClosed(){
  if (this.data.cancel$) {
    this.data.cancel$.subscribe();
  }
}

更新:

我会回答你的问题,因为你显然对替代解决方案的建议不感兴趣,但请注意答案底部的评论!

你在内存问题上不是很擅长。当组件被销毁时,你的 _result 属性 上还有一个活的 ReplaySubject。我建议您也完成并取消订阅 ngOnDestroy 生命周期挂钩中的 ReplaySubject。另一个问题是您在 show() 调用中订阅了 observable in 但您没有取消订阅。由于您使用 Subject ,因此可观察到的是多播,这意味着您在那里创建了内存泄漏。您可以考虑使用 take(1)first() 之类的管道使其成为单播,这可能是正确的做法,因为您可能希望每个打开的对话框只收到一次通知。

您也可以考虑使用普通的 Subject,因为这里不需要重放功能(至少对于您共享的代码,我看不出为什么)。

然后就是这一行:

return this._obsResult.asObservable();

大概应该是:

return this._result.asObservable();

我建议进行以下更改:

更改主题

private _result: Subject<DialogResult>;

ngOnDestroy

添加完整的主题并取消订阅
ngOnDestroy() {
  if(this._obsResult) {
    this._obsResult.complete();
    this._obsResult = null;
  }
  if (this._result) {
    this._result.complete();
    this._result.unsubscribe();
  }
}

和return单播值而不是多播使用take(1)管道:

return this._result.asObservable().pipe(take(1));

最后一条评论:

在我看来,您过度设计了一个简单的对话框。我建议重新考虑并考虑简化您的弹出逻辑。