Angular 角度2-表单组值更改取消订阅
我有一个FormGroup with ValueChanges事件,当用户从组件的路径移动到另一个组件,然后返回到该组件时,该事件不会从内存中释放 这意味着,如果用户离开组件,然后返回组件5次,onFormChange方法将触发5次,但这些调用中只有1次是针对当前组件的 我认为问题在于我需要在NgDestroy事件中取消valueChanges事件的订阅,但是valueChanges事件上没有可用的取消订阅方法 我肯定我必须取消订阅或释放内存,但我不确定是什么Angular 角度2-表单组值更改取消订阅,angular,rxjs,angular2-forms,Angular,Rxjs,Angular2 Forms,我有一个FormGroup with ValueChanges事件,当用户从组件的路径移动到另一个组件,然后返回到该组件时,该事件不会从内存中释放 这意味着,如果用户离开组件,然后返回组件5次,onFormChange方法将触发5次,但这些调用中只有1次是针对当前组件的 我认为问题在于我需要在NgDestroy事件中取消valueChanges事件的订阅,但是valueChanges事件上没有可用的取消订阅方法 我肯定我必须取消订阅或释放内存,但我不确定是什么 import * as _ fro
import * as _ from 'lodash';
import {Observable} from 'rxjs/Rx';
import {Component, Input, Output, EventEmitter, OnInit, OnDestroy} from '@angular/core';
import {FormGroup} from '@angular/forms';
import {formConfig} from './toolbar.form-config';
import {JobToolbarVm} from '../view-model/job-toolbar.vm';
import {BroadcastService} from '../../../services/broadcast/broadcast.service';
@Component({
selector: 'wk-job-toolbar',
template: require('./toolbar.html'),
})
export class JobToolbarComponent implements OnInit, OnDestroy {
protected form: FormGroup;
@Input()
toolbar: JobToolbarVm;
@Output()
toolbarChanged = new EventEmitter<JobToolbarVm>();
@Output()
refresh = new EventEmitter<string>();
constructor(private broadcast: BroadcastService) {
}
ngOnInit() {
this.form = formConfig;
this.form.setValue(this.toolbar, {onlySelf: true});
// This ALWAYS RUNS when the form loads, ie. on the job route
console.log('FORM VALUE');
console.log(JSON.stringify(this.form.value, null, 2));
this.form.valueChanges
.debounceTime(2000)
.subscribe(
this.onFormChange.bind(this)
);
}
ngOnDestroy() {
//this.form.valueChanges.unsubscribe();
//this.onChanges.unsubscribe();
//this.toolbarChanged.unsubscribe();
//this.form = null;
}
onFormChange(data: any) {
// This runs whenever I go to a different route and then come back to this route
// There is also a memory leak, because this method fires multiple times based on how
// often I navigate away and come back to this route.
// e.g. Navigate away and then back 5 times, then I see this log statement 5 times
console.log('FORM VALUE2 - THIS KEEPS FIRING FOR EACH INSTANCE OF MY COMPOMENT');
console.log(JSON.stringify(this.form.value, null, 2));
JobToolbarVm.fromJsonIntoInstance(data, this.toolbar);
this.onChanges('data-changed');
}
onChanges($event: any) {
console.log('onChanges: ' + $event);
// console.log(this.toolbar);
// Send the toolbar object back out to the parent
this.toolbarChanged.emit(this.toolbar);
// Broadcast an event that will be listened to by the list component so that it knows when to refresh the list
this.broadcast.broadcast('job-admin-toolbar-changed', this.toolbar);
}
}
从“lodash”导入*as uuu;
从'rxjs/Rx'导入{Observable};
从“@angular/core”导入{Component,Input,Output,EventEmitter,OnInit,OnDestroy};
从'@angular/forms'导入{FormGroup};
从“./toolbar.form config”导入{formConfig};
从“../view model/job toolbar.vm”导入{JobToolbarVm};
从“../../../services/broadcast/broadcast.service”导入{BroadcastService};
@组成部分({
选择器:“工作作业工具栏”,
模板:需要('./toolbar.html'),
})
导出类JobToolbarComponent实现OnInit、OnDestroy{
受保护形式:FormGroup;
@输入()
工具栏:JobToolbarVm;
@输出()
toolbarChanged=新的EventEmitter();
@输出()
刷新=新的EventEmitter();
构造函数(专用广播:广播服务){
}
恩戈尼尼特(){
this.form=formConfig;
this.form.setValue(this.toolbar,{onlySelf:true});
//这总是在表单加载时运行,即在作业路线上
console.log('formvalue');
log(JSON.stringify(this.form.value,null,2));
此.form.valueChanges
.debounceTime(2000年)
.订阅(
this.onFormChange.bind(this)
);
}
恩贡德斯特罗(){
//this.form.valueChanges.unsubscribe();
//this.onChanges.unsubscribe();
//此.toolbar已更改。取消订阅();
//this.form=null;
}
onFormChange(数据:任意){
//每当我走到另一条路线,然后又回到这条路线时,它就会运行
//还有一个内存泄漏,因为此方法根据触发方式多次触发
//我经常离开,然后回到这条路线。
//例如,导航离开,然后返回5次,然后我看到此日志语句5次
log('formvalue2-这会为我的组件的每个实例持续激发');
log(JSON.stringify(this.form.value,null,2));
JobToolbarVm.fromJSONINTONTORANCE(数据,this.toolbar);
此项。一旦更改(“数据更改”);
}
onChanges($event:any){
log('onChanges:'+$event);
//console.log(这个工具栏);
//将工具栏对象发送回父对象
this.toolbarChanged.emit(this.toolbar);
//广播列表组件将侦听的事件,以便它知道何时刷新列表
this.broadcast.broadcast('job-admin-toolbar-changed',this.toolbar');
}
}
调用subscribe()
返回一个订阅
,这就是您用来取消订阅的内容:
class JobToolbarComponent
private subscr:Subscription;
ngOnInit() {
...
this.subscr = this.form.valueChanges ...
...
}
ngOnDestroy() {
this.subscr.unsubscribe();
}
}
我创建了以下函数
export function AutoUnsubscribe(exclude = []) {
return function (constructor) {
const original = constructor.prototype.ngOnDestroy;
constructor.prototype.ngOnDestroy = function () {
for (let prop in this) {
const property = this[prop];
if (!exclude.includes(prop)) {
if (property && (typeof property.unsubscribe === "function")) {
property.unsubscribe();
}
}
}
original && typeof original === 'function' && original.apply(this, arguments);
};
}
}
实际上,您可以使用它来自动取消订阅所有观察者,但您必须将它们存储在公共属性中,以便此函数可以拦截它并对其调用取消订阅。下面介绍了如何使用它:-
@AutoUnsubscribe()
@Component({
selector: 'account-login',
templateUrl: './login.component.html',
styleUrls: ['./login.component.scss']
})
export class LoginComponent implements OnInit {
public submitWatcher: Subscription;
submit() {
this.submitWatcher = this.authService.login(this.loginForm.getRawValue())
.subscribe(res => {
if (this.returnUrl) {
this.router.navigate([this.returnUrl]);
}
else {
this.router.navigate(['/special']);
}
}, (error) => {
alert(JSON.stringify(error.data));
});
}
}
关于如何使用decorator的更多信息,请阅读这个博客,这是我的灵感来源,它非常酷
我不确定这是否是一个好主意,但这很容易实施,而且对我的学校项目很有效
var sub = this.items.subscribe(snapshots => {
console.log(snapshots);
sub.unsubscribe();
});
资料来源:
您可以执行以下操作:
阿图尔。。我喜欢定制装饰师的想法。。。但在本例中,使用take、takeWhile和takeUntil操作符更有意义。使用上述装饰器不是取消订阅的有效方法。。。你觉得怎么样?你好!非常感谢你的回答。我经常读它们。您已订阅github。但是,您能解释一下,如果组件和表单本身被破坏,我们为什么需要取消订阅吗?垃圾收集器不应该从内存中删除订阅吗?谢谢:-)通常,如果您强制订阅,强制取消订阅是一种很好的做法。也可能是垃圾收集不会立即处理组件实例,然后在它最终消失之前可能仍会收到一些事件。subscribe回调可能会在组件不再处于活动状态时导致错误,或者可能会导致在组件被释放时不再需要的昂贵操作(网络请求)。@GünterZöchbauer Ok。谢谢!;)当组件中创建了
this.form
时,哪个事件会在组件销毁后触发ValueChange
?我无法想象我需要取消订阅自己创建的表单的情况。如果unsubscribe
可以在debounceTime
之后停止流量,这也会很有趣(参见上面的示例)。顺便说一句,你的答案是正确的-我的评论更多的是关于“我们必须取消订阅吗?”。一般的规则是,如果你订阅(显式锁定资源),你就显式地释放它。当然,在某些情况下,这可能不是必需的,但通常代码在以后会以一种需要发布的方式进行修改,但很容易被忽略,并且可能导致难以发现的bug。这只是为了避免bug而进行的防御性编程。
// private variable to hold all your subscriptions for the component
private subscriptions: Subscription[] = [];
// when you subscribe to an observable,
// you can push all subscription this.subscriptions
this.subscriptions.push(
this.form.valueChanges.pipe(
.debounceTime(2000))
.subscribe(val => this.onFormChange.bind(this)),
this.observe2$.subscribe(val => this.somemethod(val))
);
// in ngondestroy
ngOnDestroy(): void {
if (this.subscriptions && this.subscriptions.length > 0) {
this.subscriptions.forEach(s => s.unsubscribe());
}
}