【问题标题】:How to get a directive to react to an EventEmitter in a component如何获取指令以对组件中的 EventEmitter 做出反应
【发布时间】:2020-07-13 08:27:15
【问题描述】:

如果对后端 api 的 http 请求返回错误,我有一个 CustomComponent 会发出一个值(我们称之为“错误”)。如何获取应用于此表单的指令(称为表单指令)以识别 CustomComponent 何时发出“错误”值?

CustomComponent 的代码:

export class CustomComponent extends FormComponent<Custom> {

  constructor(
    protected fb: FormBuilder,
    private httpService: HttpService) {
    super(fb);
  }

  currentVal: string = '';
  inputType: string = 'password';
  showPasswordTitle: string = 'Show Password';
  showPasswordStatus: boolean = false;
  form: FormGroup;

  @Output() invalidOnError = new EventEmitter<string>();

  protected buildForm(): FormGroup {
    return this.form = this.fb.group({
      fieldA: ['', Validators.required],
      fieldB: ['', Validators.required],
      fieldC: [''],
      fieldD: ['', [Validators.required, Validators.pattern('[0-9]{10}')]]
    }

  protected doSubmit(): Observable<Custom> {
    return this.httpService.callDatabase<Custom>('post', '/api/users/custom', this.value);
  };

  protected get value(): Registration {
    return {
      fieldA: this.fieldA.value,
      fieldB: this.fieldB.value,
      fieldC: this.fieldC.value,
      fieldD: this.fieldD.value
    };
  }

  get fieldA() { return this.form.get('fieldA'); }
  get fieldB() { return this.form.get('fieldB'); }
  get fieldC() { return this.form.get('fieldC'); }
  get fieldD() { return this.form.get('fieldD'); }

  protected onError() {
    if (this.error.length) {//error.length indicates some of the fields in the form are already registered in the database
      Object.keys(this.error).forEach(element => {
        let formControl = this.form.get(this.error[element])
        this.currentVal = formControl.value;
        formControl.setValidators(formControl.validator ? [formControl.validator, unique(this.currentVal)] : unique(this.currentVal))
        formControl.updateValueAndValidity()
        this.invalidOnError.emit('error');
      })
    }
  }

FormComponent 的代码:

export abstract class FormComponent<T> implements OnInit {
  protected form: FormGroup = null;
  submitted = false;
  completed = false;
  error: string = null;

  constructor(protected fb: FormBuilder) {}

  ngOnInit() {
    this.form = this.buildForm();
  }

  onSubmit() {
    this.submitted = true;
    if (this.form.valid) {
      this.doSubmit().subscribe(
        () => {
          this.error = null;
          this.onSuccess();
        },
        err => {
          this.error = err
          this.onError();
        },
        () => {
          this.submitted = false;
          this.completed = true;
        }
      )
    }
  }

  protected abstract get value(): T;
  protected abstract buildForm(): FormGroup;
  protected abstract doSubmit(): Observable<T>;

  protected onSuccess() {}
  protected onError() {}
}

表单指令代码(当用户点击提交按钮时效果很好,这会触发 CustomComponent 中的 onSubmit 事件):

@Directive({
  selector: 'form'
})
export class FormSubmitDirective {
  submit$ = fromEvent(this.element, 'submit').pipe(shareReplay(1));

  constructor(private host: ElementRef<HTMLFormElement>) {}

  get element() {
    return this.host.nativeElement;
  }
}

我希望这样的事情可以解决我的问题,但这肯定行不通。

invalidOnError$ = fromEvent(this.element, 'error').pipe(shareReplay(1));

这个想法是使用指令中的 submit$ 或 invalidOnError$ 来关注表单中的第一个无效字段。对 submit$ 有效,但对 invalidOnError$ 无效。感谢一些帮助 - 对 Angular 来说还是个新手。

【问题讨论】:

    标签: angular9


    【解决方案1】:

    通过在另一个表单指令中使用 @Input 装饰器,我得到了这个以一种全面的方式工作,该指令也从表单指令导入 submit$。

    FormComponent 和 Form Directive 的代码与问题中显示的内容相比没有变化。

    来自自定义组件的相关代码:

    export class CustomComponent extends FormComponent<Custom> {
      invalidOnError: string = '';
      form: FormGroup;
    
      protected buildForm(): FormGroup {
        return this.form = this.fb.group({
          fieldA: ['', Validators.required],
          fieldB: ['', Validators.required],
          fieldC: [''],
          fieldD: ['', [Validators.required, Validators.pattern('[0-9]{10}')]]
        }
    
      protected doSubmit(): Observable<Custom> {
        invalidOnError = '';
        return this.httpService.callDatabase<Custom>('post', '/api/users/custom', this.value);
      };
    
      protected get value(): Registration {
        return {
          fieldA: this.fieldA.value,
          fieldB: this.fieldB.value,
          fieldC: this.fieldC.value,
          fieldD: this.fieldD.value
        };
      }
    
      get fieldA() { return this.form.get('fieldA'); }
      get fieldB() { return this.form.get('fieldB'); }
      get fieldC() { return this.form.get('fieldC'); }
      get fieldD() { return this.form.get('fieldD'); }
    
      protected onError() {
        if (this.error.length) {//error.length indicates some of the fields in the form are already registered in the database
          invalidOnError = 'invalid'
          Object.keys(this.error).forEach(element => {
            let formControl = this.form.get(this.error[element])
            this.currentVal = formControl.value;
            formControl.setValidators(formControl.validator ? [formControl.validator, unique(this.currentVal)] : unique(this.currentVal))
            formControl.updateValueAndValidity()
            this.invalidOnError.emit('error');
          })
        }
      }
    

    来自CustomComponentTemplate的相关代码:

    <form class="bg-light border" appFocus="FieldA" [formGroup]="CustomForm" 
    [invalidOnError]="invalidOnError" (ngSubmit)="onSubmit()">
    

    invalidFormControlDirective的相关代码(从Form Directive导入submit$):

    @Directive({
      selector: 'form[formGroup]'
    })
    export class FormInvalidControlDirective {
      private form: FormGroup;
      private submit$: Observable<Event>;
      @Input() invalidOnError: string = ''; //this is the @Input variable invalidOnError
    
      constructor(
        @Host() private formSubmit: FormDirective,
        @Host() private formGroup: FormGroupDirective,
        @Self() private el: ElementRef<HTMLFormElement>
      ) {
        this.submit$ = this.formSubmit.submit$;
      }
    
      ngOnInit() {
        this.form = this.formGroup.form;
        this.submit$.pipe(untilDestroyed(this)).subscribe(() => {
          if (this.form.invalid) {
            const invalidName = this.findInvalidControlsRecursive(this.form)[0];
            this.getFormElementByControlName(invalidName).focus();
          }
        });
      }
    
      ngOnChanges(){
        of(this.invalidOnError).pipe(filter(val => val == 'invalid')).subscribe(() => {
            if (this.form.invalid) {
              const invalidName = this.findInvalidControlsRecursive(this.form)[0];
              this.getFormElementByControlName(invalidName).focus();
            }
          });
      }
    
      ngOnDestroy() { }
    
      // findInvalidControlsRecursive and getFormElementByControlName defined functions to get invalid controls references
    }
    

    也就是说,我有兴趣 1) 以某种方式将 onChanges 生命周期下的代码带入 invalidFormControlDirective 中的 ngOnInit 生命周期(无法使其工作),以及 2) 找出是否有某种方法可以发出事件并使用 Rxjs fromEventPattern 处理它,而不是将@Input 变量 invalidOnError 传递给 invalidFormControlDirective。

    【讨论】:

      猜你喜欢
      • 2021-12-31
      • 2012-04-22
      • 2014-02-02
      • 1970-01-01
      • 1970-01-01
      • 2021-02-06
      • 2021-04-04
      • 2019-05-07
      • 2019-04-11
      相关资源
      最近更新 更多