带有回调的角度@Output

时间:2019-06-17 07:55:44

标签: angular callback rxjs eventemitter

是否可以使用@Output进行回调?

我有一个FormComponent,用于检查有效性,并在提交时禁用“提交”按钮。现在,我想在提交完成后重新启用“提交”按钮。

@Component({
  template: `
    <form [formGroup]="form" (ngSubmit)="onSubmit()">
      ...
    </form>
  `
})
class FormComponent {
  form: FormGroup = ...;

  isSubmitting = false;

  @Output()
  submitted = new EventEmitter<MyData>()

  onSubmit() {
    if(this.form.invalid || this.isSubmitting) {
      return;
    }

    this.isSubmitting = true;

    this.submitted.emit(this.form.value);
    // Here I'd like to listen for the result of the parent component
    // something like this...
    // this.submitted.emit(...).subscribe(res => this.isSubmitting = false);
  }
}
@Component({
  template: `
    <my-form (submitted)="onSubmitted($event)"></my-form>
  `
})
class ParentComponent {
  constructor(private service: MyService) { }

  onSubmitted(event: MyData) {
    this.service.doSomething(event).pipe(
      tap(res => console.log("service res", res)
    );
    // basically I'd like to `return` this `Observable`,
    // so the `FormComponent` can listen for the completion
  }
}

我知道,我可以在@Input()中使用FormComponent并执行以下操作:

@Input()
set submitted(val: boolean) {
  this.isSubmitted = val;
}

但是我想知道是否有一个更简单/更好的解决方案,因为isSubmitted应该是FormComponent的内部属性,应该由组件本身而不是其父级来管理。 / p>

5 个答案:

答案 0 :(得分:1)

我不知道这样的回调。充其量,您可以做的是@Input接线。

argv[argvcount] = NULL;

parent.component.html

<my-form (submitted)="formSubmit($event)" [disableButton]="disableButton"></my-form>

parent.component.ts

disableButton: boolean = false; formSubmit(myForm) { this.disableButton = true; --> disable it here as soon as form submitted. this.service.doSomething(event).pipe( tap(res => { console.log("service res", res); this.disableButton = false; // --> enable it here when form submission complete } )); }

child.component.ts

@Input() disableButton: boolean

child.component.html

所以一种方法是在这些行上实现。

答案 1 :(得分:1)

 onSubmit() {
    this.isSubmitting = true;
    this.submitHandler(this.form.value).subscribe(res => {
      this.isSubmitting = false;
      this.cdr.markForCheck();
    });
  }

在上面的示例代码中,函数onSubmit()不是无状态函数,而是依赖于外部处理程序。从测试的角度来看,使函数本身不可预测。如果失败(如果失败),您将不知道在哪里,原因或方式。在组件被销毁后 ,回调也将被执行。

禁用的问题是组件使用者的外部状态。所以我只是将其设为输入绑定(就像这里的其他答案一样)。这使组件更,并且更易于测试。

@Component({
  template: `<form [formGroup]="form" (ngSubmit)="form.valid && enabled && onSubmit()"</form>`
})
class FormComponent {
  form: FormGroup = ...;

  @Input()
  enabled = true;

  @Output()
  submitted = new EventEmitter<MyData>()

  onSubmit() {
    // I prefer to do my blocking in the template
    this.submitted.emit(this.form.value);
  }
}

此处的主要区别在于,我在下面使用enabled$ | async支持OnPush更改检测。由于组件的 state 异步更改。

@Component({
  template: `<my-form [enabled]="enabled$ | async" (submitted)="onSubmitted($event)"></my-form>`
})
class ParentComponent {
  public enabled$: BehaviorSubject<boolean> = new BehaviorSubject(true);

  constructor(private service: MyService) { }

  onSubmitted(event: MyData) {
    this.enabled$.next(false);
    this.service.doSomething(event).pipe(
      tap(res => this.enabled$.next(true)
    ).subscribe(res => console.log(res));
  }
}

答案 2 :(得分:0)

您可以在父组件中设置isSubmiting并将其作为输入提供给子组件。在您的情况下,将在父组件中初始化isSubmitting并将其设置为false。然后,当从子组件中发出时,在父回调的第一行上将isSubmitting设置为true。一旦完成onSubmitted的逻辑,您就可以再次将isSubmitting设置为false。您在子组件中要做的就是接收isSubmitted作为输入,并将其设置为输入类型,并以bind attr [disabled] =“ isSubmitting”

的形式提交。

答案 3 :(得分:0)

我找到了另一种方法:通过将处理程序函数传递为@Input

class FormComponent {
  form: FormGroup = ...;

  isSubmitting = false;

  @Input()
  submitHandler: (value: MyData) => Observable<any>;

  constructor(private cdr: ChangeDetectorRef) { }

  onSubmit() {
    if (!this.form.valid || this.isSubmitting) {
      return;
    }

    this.isSubmitting = true;

    // don't forget to unsubscribe on destroy
    this.submitHandler(this.form.value).subscribe(res => {
      this.isSubmitting = false;
      this.cdr.markForCheck();
    });
  }
}
@Component({
  template: `
    <my-form [submitHandler]="submitHandler"></my-form>
  `
})
class ParentComponent {
  constructor(private service: MyService) { }

  submitHandler = (formValue: MyData): Observable<any> => {
    return this.service.doSomething(event);
  };
}

易于使用,效果很好。唯一的“坏”事情是,感觉像我在滥用@Input并非它设计的东西。

答案 4 :(得分:0)

另一个使用模板变量的解决方案:

@Component({
  template: `
    <my-form (submitted)="onSubmit($event, form)" #form></my-form>
  `
})
class ParentComponent {
  constructor(private service: MyService) { }

  onSubmit(event: MyData, form: FormComponent) {
    // don't forget to unsubscribe
    this.service.doSomething(event).pipe(
      finalize(() => {
        form.setSubmitting(false);
      })
    ).subscribe();
  }
}
class FormComponent {
  form: FormGroup = ...;

  isSubmitting = false;

  @Output()
  submitted = new EventEmitter<MyData>()

  constructor(private cdr: ChangeDetectorRef) { }

  setSubmitting(val: boolean) {
    this.isSubmitting = val;
    this.cdr.markForCheck();
  }

  onSubmit() {
    if (!this.form.valid || this.isSubmitting) {
      return;
    }

    this.isSubmitting = true;

    this.submitted.emit(this.form.value);
  }
}