[英]Angular 2 - FormGroup ValueChanges Unsubscribe
I have a FormGroup with ValueChanges event that is not being released from memory when the user moves from component's route to another component and then they return to the component.我有一个带有 ValueChanges 事件的 FormGroup,当用户从组件的路由移动到另一个组件然后他们返回到该组件时,该事件没有从内存中释放。
What this means is that if the user navigates away from component and then back to the component 5 times, the onFormChange method fires 5 times, but only 1 of those calls is for the current component.这意味着如果用户 5 次离开组件然后返回组件,onFormChange 方法会触发 5 次,但其中只有 1 次调用是针对当前组件的。
I figured that the problem was that I needed to unsubscribe from the the valueChanges event in the NgDestroy event, but there is no unsubscribe method available on the valueChanges event.我认为问题在于我需要取消订阅 NgDestroy 事件中的 valueChanges 事件,但 valueChanges 事件中没有可用的取消订阅方法。
I am sure I have to unsubscribe or release memory for something, but I'm not sure what.我确定我必须取消订阅或释放内存,但我不确定是什么。
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);
}
}
The subscribe()
call returns a Subscription
and this is what you use to unsubscribe: subscribe()
调用返回一个Subscription
,这是您用来取消订阅的:
class JobToolbarComponent
private subscr:Subscription;
ngOnInit() {
...
this.subscr = this.form.valueChanges ...
...
}
ngOnDestroy() {
this.subscr.unsubscribe();
}
}
I have created this following function我创建了以下功能
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);
};
}
}
which actually you can use to auto unsubscribe all the watchers but you have to store them in public properties so that this function can intercept it and invoke unsubscribe on that.实际上,您可以使用它来自动取消订阅所有观察者,但您必须将它们存储在公共属性中,以便该函数可以拦截它并对其调用取消订阅。 How you use it is mentioned below:-
您如何使用它如下所述:-
@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));
});
}
}
For more info on how to use decorator please read this blog that's where I have taken the idea from and it is pretty cool有关如何使用装饰器的更多信息,请阅读这个博客,这是我从中获得这个想法的地方,它非常酷
You could do the following:
您可以执行以下操作:
// 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());
}
}
I am not sure if this is a good idea but this is very simple to implement and it works well for my school project.我不确定这是否是一个好主意,但这很容易实现,并且适用于我的学校项目。
var sub = this.items.subscribe(snapshots => {
console.log(snapshots);
sub.unsubscribe();
});
Source: https://github.com/angular/angularfire2/issues/377来源: https ://github.com/angular/angularfire2/issues/377
The easiest to put together the subscriptions is to use the add method : subscription.add( anotherSubscription ) so now you just have to unsubscribe from only one subscription.将订阅组合在一起的最简单方法是使用 add 方法:subscription.add( anotherSubscription ) 所以现在您只需取消订阅一个订阅。
You can find the following example from the official doc : https://rxjs.dev/guide/subscription您可以从官方文档中找到以下示例: https ://rxjs.dev/guide/subscription
import { interval } from 'rxjs';
const observable1 = interval(400);
const observable2 = interval(300);
const subscription = observable1.subscribe(x => console.log('first: ' + x));
const childSubscription = observable2.subscribe(x => console.log('second: ' + x));
subscription.add(childSubscription);
setTimeout(() => {
// Unsubscribes BOTH subscription and childSubscription
subscription.unsubscribe();
}, 1000);
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.