Angular 6 Reactive Forms : 如何将焦点设置在第一个无效输入上
Angular 6 Reactive Forms : How to set focus on first invalid input
在我的 Angular 6 应用程序下,我正在使用 Reactive Forms .
我的目的是提交时,我想在错误时将焦点设置在第一个无效输入上。
我的表格是这样的:
<form [formGroup]="addItemfForm " (ngSubmit)="onSubmitForm()">
<div class="form-inline form-group">
<label class="col-md-2 justify-content-start">
Libellé du pef
<span class="startRequired mr-1"> *</span>
</label>
<input type="text" maxlength="100" formControlName="libellePef" class="col-md-6 form-control" placeholder="saisie obligatoire" [ngClass]="{ 'is-invalid': submitted && formFiels.libellePef.errors }" />
<div *ngIf="submitted && formFiels.libellePef.errors" class="col invalid-feedback">
<div class="col text-left" *ngIf="formFiels.libellePef.errors.required">Libellé du pef est requis.</div>
</div>
</div>
<div class="form-inline form-group">
<label class="col-md-2 justify-content-start">
Code Basicat
<span class="startRequired mr-1"> *</span>
</label>
<input type="text" maxlength="100" formControlName="codeBasicat" class="col-md-3 form-control" placeholder="saisie obligatoire" [ngClass]="{ 'is-invalid': submitted && formFiels.codeBasicat.errors }" />
<div *ngIf="submitted && formFiels.codeBasicat.errors" class="col invalid-feedback">
<div class="text-left" *ngIf="formFiels.codeBasicat.errors.required">Code Basicat est requis.</div>
</div>
</div>
<div class="form-inline form-group">
<label class="col-md-2 justify-content-start">
Nom de l'application
<span class="startRequired mr-1"> *</span>
</label>
<input type="text" maxlength="100" formControlName="nomApplication" class="col-md-6 form-control" placeholder="saisie obligatoire" [ngClass]="{ 'is-invalid': submitted && formFiels.nomApplication.errors }" />
<div *ngIf="submitted && formFiels.nomApplication.errors" class="col invalid-feedback">
<div class="text-left" *ngIf="formFiels.nomApplication.errors.required">Nom de l'application est requis.
</div>
</div>
</div>
</form>
在我的 TS 文件下,我的表单配置如下所示:
this.addItemfForm = this.formBuilder.group({
libellePef: ['', Validators.required],
codeBasicat: ['', Validators.required ],
nomApplication: ['', Validators.required ],
urlCible: [''],
modeTransfert: [''],
});
我试过 自动对焦 指令,但没有用
有什么建议吗?
我的回答灵感来自 yurzui's answer 这里。我正在使用他的回答中的逻辑来获取特定 FormControl
的 nativeElement
,方法是使用它的 FormControl
.
这是这样做的逻辑:
const originFormControlNameNgOnChanges = FormControlName.prototype.ngOnChanges;
FormControlName.prototype.ngOnChanges = function () {
const result = originFormControlNameNgOnChanges.apply(this, arguments);
this.control.nativeElement = this.valueAccessor._elementRef.nativeElement;
return result;
};
现在,表单的错误字段将为空,即使它的字段无效。因此,要获得第一个无效的确切字段,我们必须遍历所有字段并检查每个字段的有效性。我可以在 onSubmitForm()
方法中编写此逻辑。像这样:
onSubmitForm() {
const fieldsToCheck = [
'codeBasicat',
'libellePef',
'nomApplication'
];
for (let i = 0; i < fieldsToCheck.length; i++) {
const fieldName = fieldsToCheck[i];
if (this.addItemfForm.get(fieldName).invalid) {
( < any > this.addItemfForm.get(fieldName)).nativeElement.focus();
break;
}
}
}
我故意使用 for
而不是 Array.forEach
因为我想跳出循环。
希望这对您有用。
Here's a Working Sample StackBlitz for your ref.
我使用指令做到了这一点。所以我的表格看起来像这样:
<form [formGroup]="userForm" (submit)="saveData()" appFocus >
...
</form>
以及指令本身的代码:
import { Directive, HostListener, Input, ElementRef } from '@angular/core';
import { NgForm } from '@angular/forms';
@Directive({
selector: '[appFocus]'
})
export class FocusDirective {
constructor(private el: ElementRef) { }
@Input() formGroup: NgForm;
@HostListener('submit', ['$event'])
public onSubmit(event): void {
if ('INVALID' === this.formGroup.status) {
event.preventDefault();
const formGroupInvalid = this.el.nativeElement.querySelectorAll('.ng-invalid');
(<HTMLInputElement>formGroupInvalid[0]).focus();
}
}
}
但是这个解决方案是不完整的,因为有很多极端情况需要考虑。例如,如果第一个元素是单选按钮组怎么办。调度焦点事件将自动标记已归档。其次,并非 angular ads ng-invalid 的每个元素都将作为输入。
这个选项对我不起作用,但我通过如下更改代码设法修复了它:
@Directive({
selector: '[appErrorFocusin]'
})
export class ErrorFocusinDirective {
selectorToFocus : String = 'textArea,mat-select,select,input,button';
constructor(private el: ElementRef,
@Inject(DOCUMENT) private document: Document) { }
@Input() formGroup: NgForm;
@HostListener('submit', ['$event'])
public onSubmit(event): void {
if ('INVALID' === this.formGroup.status) {
event.preventDefault();
const formGroupInvalid = this.el.nativeElement.querySelectorAll('.ng- invalid,:not(.mat-badge-hidden).mat-badge');
let elementToOffset = this.getElementToOffset(formGroupInvalid[0]);
this.document.documentElement.scrollTop = elementToOffset.offsetTop;
this.setFocusOnError(elementToOffset);
}
}
getElementToOffset(element : any){
let defaultElement = element;
while (!(element.parentElement instanceof HTMLFormElement)){
if (element.parentElement){
element = element.parentElement;
}else{
return defaultElement;
}
}
return element;
}
setFocusOnError(elementToOffset : any){
let listaElementos = elementToOffset.querySelectorAll(this.selectorToFocus);
if (listaElementos.length>0){
listaElementos[0].focus();
}
}
}
试试这个:
import { Directive, HostListener, ElementRef} from '@angular/core';
@Directive({
selector: '[focusFirstInvalidField]'
})
export class FocusFirstInvalidFieldDirective {
constructor(private el: ElementRef) { }
@HostListener('submit')
onFormSubmit() {
const invalidElements = this.el.nativeElement.querySelectorAll('.ng-invalid');
if (invalidElements.length > 0) {
console.log(invalidElements[0]);
invalidElements[0].focus();
}
}
}
记得debug,看元素0是不是我遇到的自己的form,所以看对了第一个报的是什么字段,把位置放对了。
我们只需在表单的 submit() 中编写此代码,即可将焦点设置在第一个无效输入上。
if(this.form.invalid)
{
// Got focus to the error field
let invalidFields = [].slice.call(document.getElementsByClassName('ng-invalid'));
invalidFields[1].focus();
}
在您的提交中使用以下代码。
for (const key of Object.keys(this.addressForm.controls)) {
if (this.addressForm.controls[key].invalid) {
const invalidControl = this.el.nativeElement.querySelector('[formcontrolname="' + key + '"]');
invalidControl.focus();
break;
}
}
this.addressForm 将是您的 FormGroup。
我们这里甚至不需要指令。
在我的 Angular 6 应用程序下,我正在使用 Reactive Forms .
我的目的是提交时,我想在错误时将焦点设置在第一个无效输入上。
我的表格是这样的:
<form [formGroup]="addItemfForm " (ngSubmit)="onSubmitForm()">
<div class="form-inline form-group">
<label class="col-md-2 justify-content-start">
Libellé du pef
<span class="startRequired mr-1"> *</span>
</label>
<input type="text" maxlength="100" formControlName="libellePef" class="col-md-6 form-control" placeholder="saisie obligatoire" [ngClass]="{ 'is-invalid': submitted && formFiels.libellePef.errors }" />
<div *ngIf="submitted && formFiels.libellePef.errors" class="col invalid-feedback">
<div class="col text-left" *ngIf="formFiels.libellePef.errors.required">Libellé du pef est requis.</div>
</div>
</div>
<div class="form-inline form-group">
<label class="col-md-2 justify-content-start">
Code Basicat
<span class="startRequired mr-1"> *</span>
</label>
<input type="text" maxlength="100" formControlName="codeBasicat" class="col-md-3 form-control" placeholder="saisie obligatoire" [ngClass]="{ 'is-invalid': submitted && formFiels.codeBasicat.errors }" />
<div *ngIf="submitted && formFiels.codeBasicat.errors" class="col invalid-feedback">
<div class="text-left" *ngIf="formFiels.codeBasicat.errors.required">Code Basicat est requis.</div>
</div>
</div>
<div class="form-inline form-group">
<label class="col-md-2 justify-content-start">
Nom de l'application
<span class="startRequired mr-1"> *</span>
</label>
<input type="text" maxlength="100" formControlName="nomApplication" class="col-md-6 form-control" placeholder="saisie obligatoire" [ngClass]="{ 'is-invalid': submitted && formFiels.nomApplication.errors }" />
<div *ngIf="submitted && formFiels.nomApplication.errors" class="col invalid-feedback">
<div class="text-left" *ngIf="formFiels.nomApplication.errors.required">Nom de l'application est requis.
</div>
</div>
</div>
</form>
在我的 TS 文件下,我的表单配置如下所示:
this.addItemfForm = this.formBuilder.group({
libellePef: ['', Validators.required],
codeBasicat: ['', Validators.required ],
nomApplication: ['', Validators.required ],
urlCible: [''],
modeTransfert: [''],
});
我试过 自动对焦 指令,但没有用
有什么建议吗?
我的回答灵感来自 yurzui's answer 这里。我正在使用他的回答中的逻辑来获取特定 FormControl
的 nativeElement
,方法是使用它的 FormControl
.
这是这样做的逻辑:
const originFormControlNameNgOnChanges = FormControlName.prototype.ngOnChanges;
FormControlName.prototype.ngOnChanges = function () {
const result = originFormControlNameNgOnChanges.apply(this, arguments);
this.control.nativeElement = this.valueAccessor._elementRef.nativeElement;
return result;
};
现在,表单的错误字段将为空,即使它的字段无效。因此,要获得第一个无效的确切字段,我们必须遍历所有字段并检查每个字段的有效性。我可以在 onSubmitForm()
方法中编写此逻辑。像这样:
onSubmitForm() {
const fieldsToCheck = [
'codeBasicat',
'libellePef',
'nomApplication'
];
for (let i = 0; i < fieldsToCheck.length; i++) {
const fieldName = fieldsToCheck[i];
if (this.addItemfForm.get(fieldName).invalid) {
( < any > this.addItemfForm.get(fieldName)).nativeElement.focus();
break;
}
}
}
我故意使用 for
而不是 Array.forEach
因为我想跳出循环。
希望这对您有用。
Here's a Working Sample StackBlitz for your ref.
我使用指令做到了这一点。所以我的表格看起来像这样:
<form [formGroup]="userForm" (submit)="saveData()" appFocus >
...
</form>
以及指令本身的代码:
import { Directive, HostListener, Input, ElementRef } from '@angular/core';
import { NgForm } from '@angular/forms';
@Directive({
selector: '[appFocus]'
})
export class FocusDirective {
constructor(private el: ElementRef) { }
@Input() formGroup: NgForm;
@HostListener('submit', ['$event'])
public onSubmit(event): void {
if ('INVALID' === this.formGroup.status) {
event.preventDefault();
const formGroupInvalid = this.el.nativeElement.querySelectorAll('.ng-invalid');
(<HTMLInputElement>formGroupInvalid[0]).focus();
}
}
}
但是这个解决方案是不完整的,因为有很多极端情况需要考虑。例如,如果第一个元素是单选按钮组怎么办。调度焦点事件将自动标记已归档。其次,并非 angular ads ng-invalid 的每个元素都将作为输入。
这个选项对我不起作用,但我通过如下更改代码设法修复了它:
@Directive({
selector: '[appErrorFocusin]'
})
export class ErrorFocusinDirective {
selectorToFocus : String = 'textArea,mat-select,select,input,button';
constructor(private el: ElementRef,
@Inject(DOCUMENT) private document: Document) { }
@Input() formGroup: NgForm;
@HostListener('submit', ['$event'])
public onSubmit(event): void {
if ('INVALID' === this.formGroup.status) {
event.preventDefault();
const formGroupInvalid = this.el.nativeElement.querySelectorAll('.ng- invalid,:not(.mat-badge-hidden).mat-badge');
let elementToOffset = this.getElementToOffset(formGroupInvalid[0]);
this.document.documentElement.scrollTop = elementToOffset.offsetTop;
this.setFocusOnError(elementToOffset);
}
}
getElementToOffset(element : any){
let defaultElement = element;
while (!(element.parentElement instanceof HTMLFormElement)){
if (element.parentElement){
element = element.parentElement;
}else{
return defaultElement;
}
}
return element;
}
setFocusOnError(elementToOffset : any){
let listaElementos = elementToOffset.querySelectorAll(this.selectorToFocus);
if (listaElementos.length>0){
listaElementos[0].focus();
}
}
}
试试这个:
import { Directive, HostListener, ElementRef} from '@angular/core';
@Directive({
selector: '[focusFirstInvalidField]'
})
export class FocusFirstInvalidFieldDirective {
constructor(private el: ElementRef) { }
@HostListener('submit')
onFormSubmit() {
const invalidElements = this.el.nativeElement.querySelectorAll('.ng-invalid');
if (invalidElements.length > 0) {
console.log(invalidElements[0]);
invalidElements[0].focus();
}
}
}
记得debug,看元素0是不是我遇到的自己的form,所以看对了第一个报的是什么字段,把位置放对了。
我们只需在表单的 submit() 中编写此代码,即可将焦点设置在第一个无效输入上。
if(this.form.invalid)
{
// Got focus to the error field
let invalidFields = [].slice.call(document.getElementsByClassName('ng-invalid'));
invalidFields[1].focus();
}
在您的提交中使用以下代码。
for (const key of Object.keys(this.addressForm.controls)) {
if (this.addressForm.controls[key].invalid) {
const invalidControl = this.el.nativeElement.querySelector('[formcontrolname="' + key + '"]');
invalidControl.focus();
break;
}
}
this.addressForm 将是您的 FormGroup。
我们这里甚至不需要指令。