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));
最后一条评论:
在我看来,您过度设计了一个简单的对话框。我建议重新考虑并考虑简化您的弹出逻辑。
我想通过代码显示一个对话框(不是 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));
最后一条评论:
在我看来,您过度设计了一个简单的对话框。我建议重新考虑并考虑简化您的弹出逻辑。