NgModel 在 onBlur 和 onSubmit 上更新
NgModel update on both onBlur & onSubmit
我正在尝试创建一个表单,其输入仅更新 onBlur ([ngFormOptions]="{updateOn: 'blur'}"
)。但这样做的副作用是,一旦用户点击 'enter',表单将不再提交,因为模型仅在 Blur 上更新。 (与 this 问题相同,但没有答案)
因此,表单被标记为无效,因为输入字段中有一个值,但模型尚未用该值更新。
版本:
- Angular 6.0.7
- @angular/forms 6.0.7
HTML 示例
<form (ngSubmit)="login()" #loginForm="ngForm" [ngFormOptions]="{updateOn: 'blur'}" [validate-on-submit]="loginForm">
<input type="text" ([NgModel])="user.username"/>
<input type="password" ([NgModel])="user.password"/>
</form>
当在两个字段中输入有效文本并点击 'enter' 时,表单会验证密码(具有焦点的输入)并将其标记为无效,因为 NgModel 尚未更新,因此值无效。
我想要什么
所以我要寻找的是一种在 onBlur
和 onSubmit
上进行验证的方法。我知道因为 Angular 5 我们有选项 [ngFormOptions]="{updateOn: 'blur'}"
,但是有没有办法给这个对象 2 个参数?
([ngFormOptions]="{updateOn: ['blur', 'submit']}"
好像不行)
差点忘了,我不想更新模型onChange
(创建恼人的消息,不是很有帮助,因为用户仍在输入..)
在您的表单中创建第三个不可见的输入,并为其提供一个模板变量。在提交之前,只需关注此输入,这将触发 onBlur
更新:
<form (ngSubmit)="focuser.focus(); login()" #loginForm="ngForm" [ngFormOptions]="{updateOn: 'blur'}">
<input type="text" ([NgModel])="user.username"/>
<input type="password" ([NgModel])="user.password"/>
<input type="hidden" #focuser>
</form>
请注意,这是您可以使用的众多解决方法之一,不是实际的解决方案
解决方案——一个表单组件
在遵循@trichetriche 的建议(创建一个单独的组件来保存表单的逻辑)并让模板驱动的表单支持响应式表单之后,我找到了以下解决方案:
@Component({
selector: "val-message-form",
templateUrl: "./message-form.html"
})
export class MessageFormComponent {
/**
* The form group that holds all the input fields and the logic of the input fields
* @type {FormGroup}
*/
@Input()
public formGroup: FormGroup;
/**
* The function that will be called when the form is valid
* The template that includes this component can listen to this event for the submit action
* @type {EventEmitter}
*/
@Output()
private onSubmit = new EventEmitter();
/**
* Notify all listeners that the form has been submitted
* NOTE: Notify will only work if the form is valid
*/
public submitForm(): void {
if (this.formGroup.valid) {
this.onSubmit.emit();
} else {
this.updateControls(this.formGroup);
}
}
/**
* Recursively update all form controls, since we are going to display error messages
*/
private updateControls(formGroup: FormGroup): void {
Object.keys(formGroup.controls).forEach(field => {
const control = formGroup.get(field);
if (control instanceof FormControl) {
control.markAsTouched({onlySelf: true}); // Otherwise validation color is not shown
control.updateValueAndValidity({onlySelf: true})
} else if (control instanceof FormGroup) {
this.updateControls(control);
}
});
}
}
请注意注入的表单组,我们可以在该组件的新表单上设置它,以便正确嵌入表单控件(否则Angular无法识别表单控件)。 这就是阻止我使用模板驱动表单的问题
提交表单后,我们现在检查它是否有效。如果不是,那么我们递归循环遍历所有 FormControls (updateControls()
) 并将状态更新为 'touched'(因为我们要在它们上设置错误,我们也可以将它们标记为 'touched' ).为了让 Angular 向它的监听器发送一个 stateChange
事件,我们需要使用 updateValueAndValidity()
。由于我们已经在每个 formControl 中循环,我们可以将 onlySelf
标志设置为 true
。
属于表格的模板:
<form (ngSubmit)="submitForm()"
[formGroup]="form"
class="no-validate">
<ng-content></ng-content>
</form>
通过确保 Angular 在表单无效时发出事件,我们创建了一个可用于自定义消息的挂钩(记住 Angular [=45] 中的 ng-messages
=]?)。有关更多信息,请参见 [此处][2]。
如果你想在模板中使用它,它看起来如下:
<val-message-form (onSubmit)="login()" [formGroup]="form">
<fieldset>
<!-- Username -->
<div class="form-group">
<input type="email"
formControlName="username">
</div>
<!-- Password -->
<div class="form-group">
<input type="password"
formControlName="password">
</div>
<button type="submit" class="btn btn-success shadow-none">
{{'LOGIN_PAGE.LOGIN.SHORT' | translate}}
</button>
</fieldset>
</val-message-form>
注意 [formGroup]
标签,Angular 使用相同的标签在表单上定义反应式表单。从输入中删除标签会导致 angular 错误,告诉您它需要标签才能分配 formControlName
。我们可以确保程序员不会 运行 通过使用相同的标记将表单注入指令来解决这个问题。 Angular会很高兴,你可以拿到表格里面的指令,双赢!
我正在尝试创建一个表单,其输入仅更新 onBlur ([ngFormOptions]="{updateOn: 'blur'}"
)。但这样做的副作用是,一旦用户点击 'enter',表单将不再提交,因为模型仅在 Blur 上更新。 (与 this 问题相同,但没有答案)
因此,表单被标记为无效,因为输入字段中有一个值,但模型尚未用该值更新。
版本:
- Angular 6.0.7
- @angular/forms 6.0.7
HTML 示例
<form (ngSubmit)="login()" #loginForm="ngForm" [ngFormOptions]="{updateOn: 'blur'}" [validate-on-submit]="loginForm">
<input type="text" ([NgModel])="user.username"/>
<input type="password" ([NgModel])="user.password"/>
</form>
当在两个字段中输入有效文本并点击 'enter' 时,表单会验证密码(具有焦点的输入)并将其标记为无效,因为 NgModel 尚未更新,因此值无效。
我想要什么
所以我要寻找的是一种在 onBlur
和 onSubmit
上进行验证的方法。我知道因为 Angular 5 我们有选项 [ngFormOptions]="{updateOn: 'blur'}"
,但是有没有办法给这个对象 2 个参数?
([ngFormOptions]="{updateOn: ['blur', 'submit']}"
好像不行)
差点忘了,我不想更新模型onChange
(创建恼人的消息,不是很有帮助,因为用户仍在输入..)
在您的表单中创建第三个不可见的输入,并为其提供一个模板变量。在提交之前,只需关注此输入,这将触发 onBlur
更新:
<form (ngSubmit)="focuser.focus(); login()" #loginForm="ngForm" [ngFormOptions]="{updateOn: 'blur'}">
<input type="text" ([NgModel])="user.username"/>
<input type="password" ([NgModel])="user.password"/>
<input type="hidden" #focuser>
</form>
请注意,这是您可以使用的众多解决方法之一,不是实际的解决方案
解决方案——一个表单组件
在遵循@trichetriche 的建议(创建一个单独的组件来保存表单的逻辑)并让模板驱动的表单支持响应式表单之后,我找到了以下解决方案:
@Component({
selector: "val-message-form",
templateUrl: "./message-form.html"
})
export class MessageFormComponent {
/**
* The form group that holds all the input fields and the logic of the input fields
* @type {FormGroup}
*/
@Input()
public formGroup: FormGroup;
/**
* The function that will be called when the form is valid
* The template that includes this component can listen to this event for the submit action
* @type {EventEmitter}
*/
@Output()
private onSubmit = new EventEmitter();
/**
* Notify all listeners that the form has been submitted
* NOTE: Notify will only work if the form is valid
*/
public submitForm(): void {
if (this.formGroup.valid) {
this.onSubmit.emit();
} else {
this.updateControls(this.formGroup);
}
}
/**
* Recursively update all form controls, since we are going to display error messages
*/
private updateControls(formGroup: FormGroup): void {
Object.keys(formGroup.controls).forEach(field => {
const control = formGroup.get(field);
if (control instanceof FormControl) {
control.markAsTouched({onlySelf: true}); // Otherwise validation color is not shown
control.updateValueAndValidity({onlySelf: true})
} else if (control instanceof FormGroup) {
this.updateControls(control);
}
});
}
}
请注意注入的表单组,我们可以在该组件的新表单上设置它,以便正确嵌入表单控件(否则Angular无法识别表单控件)。 这就是阻止我使用模板驱动表单的问题
提交表单后,我们现在检查它是否有效。如果不是,那么我们递归循环遍历所有 FormControls (updateControls()
) 并将状态更新为 'touched'(因为我们要在它们上设置错误,我们也可以将它们标记为 'touched' ).为了让 Angular 向它的监听器发送一个 stateChange
事件,我们需要使用 updateValueAndValidity()
。由于我们已经在每个 formControl 中循环,我们可以将 onlySelf
标志设置为 true
。
属于表格的模板:
<form (ngSubmit)="submitForm()"
[formGroup]="form"
class="no-validate">
<ng-content></ng-content>
</form>
通过确保 Angular 在表单无效时发出事件,我们创建了一个可用于自定义消息的挂钩(记住 Angular [=45] 中的 ng-messages
=]?)。有关更多信息,请参见 [此处][2]。
如果你想在模板中使用它,它看起来如下:
<val-message-form (onSubmit)="login()" [formGroup]="form">
<fieldset>
<!-- Username -->
<div class="form-group">
<input type="email"
formControlName="username">
</div>
<!-- Password -->
<div class="form-group">
<input type="password"
formControlName="password">
</div>
<button type="submit" class="btn btn-success shadow-none">
{{'LOGIN_PAGE.LOGIN.SHORT' | translate}}
</button>
</fieldset>
</val-message-form>
注意 [formGroup]
标签,Angular 使用相同的标签在表单上定义反应式表单。从输入中删除标签会导致 angular 错误,告诉您它需要标签才能分配 formControlName
。我们可以确保程序员不会 运行 通过使用相同的标记将表单注入指令来解决这个问题。 Angular会很高兴,你可以拿到表格里面的指令,双赢!