我有一个带有 FormControl 的组件并订阅其值更改:
@Component(...)
export class FooComponent implements OnInit, OnDestroy {
fooFormControl: FormControl;
...
ngOnInit() {
this.fooFormControl.valueChanges.subscribe(
() => {...},
() => {},
() => {
// never happens
},
);
}
ngOnDestroy() {
//happens
}
}
但是当组件被销毁时,FormControl 元素不会被销毁,并且 onComplete 回调永远不会发生。
在组件销毁时销毁 FormControl 元素的正确方法是什么?
FormControl
可观察量不应该完成,尽管如果存在一些属于完整回调的逻辑,则可以手动完成此操作。
由于
valueChanges
是事件发射器并继承自 RxJS Subject
,因此可以取消订阅或完成:
ngOnDestroy() {
this.fooFormControl.valueChanges.complete();
// and/or
this.fooFormControl.valueChanges.unsubscribe();
}
未来
EventEmitter
可能不会成为主题。这可能会导致重大更改,但目前它仅依赖于 RxJS。
这里有两个想法可能会对您有所帮助。不确定它们是否是“正确的方法”,但到目前为止它对我来说效果很好。除此之外,我像往常一样在 ngOnDestroy 中取消订阅。
#1 通过 takeUntil 引入你自己的主题来强制完成
在 TypeScript 中,valueChanges 属性是 Observable 类型。如果您不想解决这个问题并进入主题,那么您可以使用 takeUntil 运算符引入您自己的主题。这将允许您强制在高于 valueChanges 可观察的级别传播完成。重要的是不要将 takeUntil 运算符放在 switchmap 之前,否则切换到 observable 将继续,并且您的订阅不会被取消!因此,我将其放在订阅运算符之前。
这是一个例子:
const stop = new Subject();
// simulate ngOnDestroy
window.setTimeout(() => {
stop.next();
stop.complete();
}, 3500);
Observable
.interval(1000)
.takeUntil(stop)
.subscribe(
() => { console.log('next'); },
() => { console.log('error'); },
() => { console.log('complete'); }
);
这是一个工作示例:https://rxviz.com/v/RoQNBnOM
#2 将 FormBuilder 添加到组件的提供者列表中
这是我通常做的事情。实际上,我通常将表单封装在使用 FormBuilder 的服务中,但效果是相同的。在此级别提供服务将在每次创建组件时销毁并重新创建该服务。我开始这样做是因为我一直遇到由可观察流引起的奇怪错误,这些错误是由 valueChanges 创建的,在组件的生命周期中持续存在。当重新创建组件之类的事情时,他们会被重新订阅。
这是一个例子:
@Component({
selector: 'my-form',
templateUrl: './my-form.component.html',
styleUrls: ['./my-form.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
providers: [ FormBuilder ] // <-- THIS PART
})
export class MyFormComponent implements OnInit, OnDestroy {
}
请注意,这实际上并不传播完成。但它确实每次都会给你一个新鲜的来源主题。
你不能破坏你的表单控件。组件内的所有内容都将随之销毁,但事件可以保留在您的应用程序中。这样您就可以取消订阅它的活动。
@Component(...)
export class FooComponent implements OnInit, OnDestroy {
fooFormControl: FormControl;
var subscriber;
...
ngOnInit() {
this.subscriber = this.fooFormControl.valueChanges.subscribe(
() => {...},
() => {},
() => {
// never happens
},
);
}
ngOnDestroy() {
this.subscriber.unsubscribe();
}
}
使用直至销毁
export class FooComponent implements OnInit {
destroyRef: DestroyRef = inject(DestroyRef);
fooFormControl: FormControl; //assuming you do have a valid instance
ngOnInit() {
this.initOnFooControlChange();
}
private initOnFooControlChange(){
this.fooFormControl.valueChanges.pipe(takeUntilDestroyed(this.destroyRef)).subscribe(
() => {},
() => {},
() => {
},
);
}
}