如果我快速点击提交按钮,表单会被提交两次或更多次。我的想法是使用disabled属性来防止这种情况,但我需要在每种形式中使用变量disableButon
:
@Component({
selector: 'example',
template: `
<form (submit)="submit()" >
<--! Some Inputs -->
<button [disabled]="disableButton" type="submit">Submit<button>
</form>
`
})
export class ExampleComponent {
private disableButton: boolean = false;
.......
submit(){
this.disableButton = true;
/*
* API call
*/
this.disableButton = false;
}
}
我这样做是对吗还是有更优质/优雅的方式来做这件事?
答案 0 :(得分:7)
处理双重提交很容易错误。在包含<form #theForm="ngForm" (ngSubmit)="submit()">
的表单上:
<button type="submit" [disabled]="theForm.submitted" />
才有效。按下按钮时,Angular的ngForm.submitted设置为true,而不是在表单通过验证后设置为true。 (NgForm的“提交”属性实际上意味着“试图提交”。)
<button type="submit" [disabled]="theForm.submitted && theForm.valid" />
并不是更好:在提交验证错误后,当用户修复验证错误时,提交按钮会自动禁用,因为它们会重新提交。
直接或通过组件ngForm.submitted
中的ngForm.resetForm()
重置submit()
是一个不好的选择,因为submitted
是您的主要变量,用于控制验证错误消息是否以及在何处显示。
真正的问题:Angular无法知道submit()
中的API调用何时或是否失败或成功。即使Angular提供的属性意味着“只需单击提交按钮并且它也通过了所有验证”,您可以挂起[disabled] =“thatProperty”,Angular将不知道何时设置属性返回,例如当您的API调用错误并且您想让用户再次按提交重新尝试服务器时。
也许Angular可能会禁止所有提交函数的格式为() => Observable<boolean>
,并且它可以订阅你的提交成功或失败,但是在框架中重置布尔值似乎有点过分。
因此,必须在所有API调用完成后执行操作,并以某种方式通知Angular提交按钮已准备好重用。该操作要么是设置您正在执行的显式布尔值,要么是强制禁用。
以下是如何在没有布尔值的情况下强制执行。
将#submitBtn等模板引用变量添加到提交按钮:
<button type="submit" #submitBtn class="green">Go!</button>
将其传递给您的组件submit()
:
<form (ngSubmit)="submit(submitBtn)" ...>
接受并使用组件方:
submit(submitBtn: HTMLButtonElement): void {
submitBtn.disabled = true;
/// API calls
submitBtn.disabled = false;
}
如果您的API调用有多个共享一个常见错误处理程序的路径,那么您还需要将HTMLButtonElement传递给它们,因为它们无法再使用{{1}将其从组件中删除}。
(或者,不是声明和传递#submitBtn,而是已经声明了#theForm,所以将其传递为:NgForm,组件代码可以向下钻取到按钮...或覆盖整个表单或者其他什么。)
这个解决方案是否优于声明另一个与this.disableButton
略有不同的布尔值的优雅意见,但事实上Angular无法知道组件的submit()及其所有异步进程何时是完成没有订阅。
答案 1 :(得分:5)
这也应该有效:
<button #button (ngSubmit)="button.disabled = true" type="submit">Submit<button>
或仅(click)
代替(ngSubmit)
更新(请参阅评论)
<button #button [disabled]="!form.valid || button.hasAttribute('is-disabled')"
(ngSubmit)="button.setAttribute('is-disabled', 'true')"
type="submit">Submit<button>
更新(使用指令)
@Directive({
selector: 'button[type=submit]'
})
class PreventDoubleSubmit {
@HostBinding() disabled:boolean = false;
@Input() valid:boolean = true;
@HostListener('click')
onClick() {
if(!valid) {
return;
}
this.disabled = true;
}
}
并像
一样使用它<button type="submit" [valid]="!form.valid">Submit<button>
您需要将其添加到要使用它的组件的directives: [PreventDoubleSubmit]
,或者全局提供
provide(PLATFORM_DIRECTIVES, {useValue: [PreventDoubleSubmit], multi: true})
答案 2 :(得分:3)
由于您在提交通话中已经在进行disableButton = true
,因此您可以在调用提交方法之前检查disableButton
。
<强>模板强>
<form (submit)="!disableButton && submit()" >
<--! Some Inputs -->
<button [disabled]="disableButton" type="submit">Submit<button>
</form>
答案 3 :(得分:3)
我有一个稍微不同的方式(也许更简单)处理这个 - 虽然适用相同的原则。
基本上我要做的是:
这是我的html模板代码:
<form #form="ngForm" (ngSubmit)="handleSubmit(form.value, form.valid)">
<button type="submit" [disabled]="form.invalid || disableButton">
Submit
</button>
</form>
这是我的班级:
export class UpdateForm {
disableButton: boolean;
constructor() { this.disableButton = false; }
handleSubmit(formData: any, isValid: boolean) {
if (isValid) {
this.disableButton = true; // the button will then be disabled
onHandleUpdate(formData);
}
}
onHandleUpdate(formData) {
this.disableButton = false; // the button will renable
}
}
答案 4 :(得分:0)
您可能有一个基本组件公开了一个get,它告诉该组件是否忙。可以在模板上使用此get来禁用或启用按钮。对于执行异步调用的操作,基本组件具有受保护的方法,该方法接受函数调用。例如
export abstract class BusyComponent {
private _isBusy = false;
get isBusy(): boolean {
return this._isBusy;
}
protected async performBusyTask<T>(busyFunction: () => Promise<T>) {
this._isBusy = true;
try {
return await busyFunction();
} finally {
this._isBusy = false;
}
}
}
class BusyComponentChild extends BusyComponent {
constructor(private dependency: Dependency) {
super();
}
doSomethingAsync(): Promise<number> {
return this.performBusyTask<number>(() => this.dependency.doSomethingAsync());
}
}
<button type="submit" [disabled]="isBusy || !form.valid">Save</button>
答案 5 :(得分:0)
如果面临双重提交,请检查模板中是否没有两次调用Submit方法。例如,它可能在您的<form>
以及提交的<button>
标签中:
<form [formGroup]="form" (ngSubmit)="onSubmit()">
// very long form content
<button type="submit" (click)="onSubmit()">Submit</button>
</form>