EventEmitter 持续发出值

EventEmitter is continuously emitting the value

我正在尝试创建一个指令,该指令在用户输入时显示加载栏,并在用户停止输入时提交输入。

import { Directive, EventEmitter, Input, Output } from '@angular/core';
import { FormGroup } from '@angular/forms';
import { debounceTime, tap } from 'rxjs/operators';
import { Subscription } from 'rxjs';

@Directive({
  selector: '[appAutoSave]',
})
export class AutoSaveDirective {
  @Input() formGroup!: FormGroup;

  private _state!: 'loading' | 'synced' | 'modified' | 'error';

  @Output() stateChange = new EventEmitter<string>();
  @Output() onSubmit = new EventEmitter();
  @Output() formError = new EventEmitter<string>();

  private formSub!: Subscription;

  ngOnInit() {
    this.preloadData();
    this.autoSave();
  }

  preloadData() {
    this.formGroup.markAsPristine();
    // this.state = 'synced';
  }

  // Autosaves form changes
  autoSave() {
    console.log('inside changes');
    this.formSub = this.formGroup.valueChanges
      .pipe(
        tap(change => {
          this.state = 'modified';
        }),
        debounceTime(2000),
        tap(change => {
          if (this.formGroup.valid && this._state === 'modified') {
            // this.onSubmit.emit();
            this.state = 'synced';
          }
        }),
      )
      .subscribe();
  }

  set state(val: any) {
    this._state = val;
    this.stateChange.emit(val);
     if (val === 'synced') {
       this.onSubmit.emit();
     }
  }
  ngOnDestroy() {
    this.formSub.unsubscribe();
  }
}
  changeState(event: string) {
    this.changeHandler.emit(event);
     if (event === 'synced') { // condition
       this.submit(); 
     }
  }

如果我删除上面的 if 条件,一切正常。但是当值同步并且用户停止输入时,我必须调用提交函数。

    <form
      [formGroup]="situationFormGroup"
      appAutoSave
      (stateChange)="changeState($event)"
    >

我会从削减这个指令责任开始。

It is doing to much .. 'loading' | 'synced' | 'modified' | 'error'. I would suggest creating 3 directives: (Keep it SOLID)

  • 加载指令
  • 错误指令
  • 打字指令

并且与 TypingDirective 相比,您只会在 form.valueChanges 的开头显示该加载程序,而在 debounceTime(2000) 之后的 tap(this.onSubmit.emit(...)) 中则不会。

在您的 autosave 方法中,您在 pipe 函数中编写了 tap 运算符,如下所示:

tap(change => {
    this.state = 'modified';
})

上面的代码对 FormGroup 中的每个 valueChanges 都会设置 state,这将发出一个 stateChange 事件。因此,对于每个更改,EventEmitter 都会发出值(请参阅 state 的 setter 中的代码)

为避免这种情况,请执行以下操作:

tap(change => {
    if(this._state!=='modified'){ 
        this.state = 'modified';
    }
})