Angular @Output 带回调
Angular @Output with callback
是否可以使用 @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
}
}
我知道,我可以在 FormComponent
中使用 @Input()
并执行如下操作:
@Input()
set submitted(val: boolean) {
this.isSubmitted = val;
}
但我想知道是否有更简单/更好的解决方案,因为isSubmitted
应该是FormComponent
的内部属性,应该由组件自己管理而不是它的 parent.
您可以在父组件中设置 isSubmiting 并将其作为输入提供给子组件。在您的情况下,解决方案是在父组件中初始化 isSubmitting 并将其设置为 false。然后,当您从子组件发出一个值时,您会在父回调的第一行将 isSubmitting 设置为 true。 onSubmitted 的逻辑完成后,您可以再次将 isSubmitting 设置为 false。您在子组件中所要做的就是接收 isSubmitted 作为输入并将其设置为输入类型 submit as bind attr [disabled]="isSubmitting"
我不知道有这样的回调。充其量你可以做一些@Input 接线。
在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
<button [disabled]="disableButton?'':null">Submit</button>
所以一种方法是在这些行上实施。
我找到了另一种方法:通过将处理函数传递为 @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);
};
}
这很容易使用,而且效果很好。唯一 "bad" 的事情是,感觉我在滥用 @Input
一些不是设计的东西。
onSubmit() {
this.isSubmitting = true;
this.submitHandler(this.form.value).subscribe(res => {
this.isSubmitting = false;
this.cdr.markForCheck();
});
}
在上面的示例代码中,函数 onSubmit()
不是 无状态函数 并且依赖于 外部 处理程序。从测试的角度来看,使函数本身不可预测。当这失败时(如果它失败了)你将不知道在哪里、为什么或如何。回调也正在执行 after 组件被 destroyed.
被禁用的问题是组件使用者的外部状态。所以我只想将它作为输入绑定(就像这里的其他答案一样)。这使得组件更 干燥 并且更容易测试。
@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));
}
}
另一个使用模板变量的解决方案:
@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);
}
}
是否可以使用 @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
}
}
我知道,我可以在 FormComponent
中使用 @Input()
并执行如下操作:
@Input()
set submitted(val: boolean) {
this.isSubmitted = val;
}
但我想知道是否有更简单/更好的解决方案,因为isSubmitted
应该是FormComponent
的内部属性,应该由组件自己管理而不是它的 parent.
您可以在父组件中设置 isSubmiting 并将其作为输入提供给子组件。在您的情况下,解决方案是在父组件中初始化 isSubmitting 并将其设置为 false。然后,当您从子组件发出一个值时,您会在父回调的第一行将 isSubmitting 设置为 true。 onSubmitted 的逻辑完成后,您可以再次将 isSubmitting 设置为 false。您在子组件中所要做的就是接收 isSubmitted 作为输入并将其设置为输入类型 submit as bind attr [disabled]="isSubmitting"
我不知道有这样的回调。充其量你可以做一些@Input 接线。
在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
<button [disabled]="disableButton?'':null">Submit</button>
所以一种方法是在这些行上实施。
我找到了另一种方法:通过将处理函数传递为 @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);
};
}
这很容易使用,而且效果很好。唯一 "bad" 的事情是,感觉我在滥用 @Input
一些不是设计的东西。
onSubmit() {
this.isSubmitting = true;
this.submitHandler(this.form.value).subscribe(res => {
this.isSubmitting = false;
this.cdr.markForCheck();
});
}
在上面的示例代码中,函数 onSubmit()
不是 无状态函数 并且依赖于 外部 处理程序。从测试的角度来看,使函数本身不可预测。当这失败时(如果它失败了)你将不知道在哪里、为什么或如何。回调也正在执行 after 组件被 destroyed.
被禁用的问题是组件使用者的外部状态。所以我只想将它作为输入绑定(就像这里的其他答案一样)。这使得组件更 干燥 并且更容易测试。
@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));
}
}
另一个使用模板变量的解决方案:
@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);
}
}