繁体   English   中英

Angular 2 - FormGroup ValueChanges 退订

[英]Angular 2 - FormGroup ValueChanges Unsubscribe

我有一个带有 ValueChanges 事件的 FormGroup,当用户从组件的路由移动到另一个组件然后他们返回到该组件时,该事件没有从内存中释放。

这意味着如果用户 5 次离开组件然后返回组件,onFormChange 方法会触发 5 次,但其中只有 1 次调用是针对当前组件的。

我认为问题在于我需要取消订阅 NgDestroy 事件中的 valueChanges 事件,但 valueChanges 事件中没有可用的取消订阅方法。

我确定我必须取消订阅或释放内存,但我不确定是什么。

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);
  }
}

subscribe()调用返回一个Subscription ,这是您用来取消订阅的:

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));
            });
    }

}

有关如何使用装饰器的更多信息,请阅读这个博客,这是我从中获得这个想法的地方,它非常酷

博客

您可以执行以下操作:

// 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());
        }
    }

我不确定这是否是一个好主意,但这很容易实现,并且适用于我的学校项目。

var sub = this.items.subscribe(snapshots => {
    console.log(snapshots);
    sub.unsubscribe();
});

来源: https ://github.com/angular/angularfire2/issues/377

将订阅组合在一起的最简单方法是使用 add 方法:subscription.add( anotherSubscription ) 所以现在您只需取消订阅一个订阅。

您可以从官方文档中找到以下示例: 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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM