Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/eclipse/9.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Angular 角度2-表单组值更改取消订阅_Angular_Rxjs_Angular2 Forms - Fatal编程技术网

Angular 角度2-表单组值更改取消订阅

Angular 角度2-表单组值更改取消订阅,angular,rxjs,angular2-forms,Angular,Rxjs,Angular2 Forms,我有一个FormGroup with ValueChanges事件,当用户从组件的路径移动到另一个组件,然后返回到该组件时,该事件不会从内存中释放 这意味着,如果用户离开组件,然后返回组件5次,onFormChange方法将触发5次,但这些调用中只有1次是针对当前组件的 我认为问题在于我需要在NgDestroy事件中取消valueChanges事件的订阅,但是valueChanges事件上没有可用的取消订阅方法 我肯定我必须取消订阅或释放内存,但我不确定是什么 import * as _ fro

我有一个FormGroup with ValueChanges事件,当用户从组件的路径移动到另一个组件,然后返回到该组件时,该事件不会从内存中释放

这意味着,如果用户离开组件,然后返回组件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);
  }
}
从“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());
        }
    }