Reputation: 1943
I have checked in stackoverflow but I only found the way to do.
I found a solution with querySelector
but is there any better approach for setting focus?
When I submit the form then I want to set focus on the first invalid input using Template Driven form.
How can I set focus on first invalid input using Template Driven forms?
Upvotes: 2
Views: 824
Reputation: 1680
SOLUTION # 1:
I just coded this and it's working at least with inputs (with type text and number) and also with selects. It's using template driven as you requested.
Here it goes:
This is the minimum that you will need in your component (.ts file):
// Built-in Packages:
import { Component, ElementRef, ViewChild } from '@angular/core';
import { NgForm } from "@angular/forms";
@Component({
selector: 'app-shopping-edit',
templateUrl: './shopping-edit.component.html',
styleUrls: ['./shopping-edit.component.css']
})
export class ShoppingEditComponent implements OnInit {
@ViewChild('f', {static: false}) ingredientForm: NgForm;
constructor(private elementRef: ElementRef) {
}
focusFirstInvalidControl(): void {
const formControls = this.ingredientForm.controls;
const formControlsKeys = Object.keys(formControls)
for (let i = 0; i < formControlsKeys.length; i++) {
const isInvalid = formControls[formControlsKeys[i]].invalid;
if (isInvalid) {
this.elementRef.nativeElement.querySelector(`#${formControlsKeys[i]}`).focus();
break;
}
}
}
}
That NgForm is connected as usual to your form on your template.
Conditions:
in order to make this work, each input inside the form in your template, needs to have a matching id, with the name and the local reference bindded to ngModel. All of them using the same name/denomination.
Example: Using a text input for the "Name".
<div class="form-group">
<label for="name">Name</label>
<input
type="text"
id="name"
name="name"
class="form-control"
placeholder="Type the name here"
autofocus
required
ngModel
#name="ngModel"
>
</div>
<span *ngIf="(name?.invalid && name.touched); else nameOk" class="help-block">Please enter a valid name</span>
<ng-template #nameOk><span class="help-block"> </span></ng-template>
</div>
Another example: Using a numeric input for the "Amount":
<div class="form-group">
<label for="amount">Amount</label>
<input
type="number"
id="amount"
name="amount"
min="1"
class="form-control"
required
[ngModel]="1"
#amount="ngModel"
>
</div>
<span *ngIf="(amount?.invalid && amount.touched); else amountOk" class="help-block">Please enter a valid amount</span>
<ng-template #amountOk><span class="help-block"> </span></ng-template>
Now you only need to trigger the method "focusFirstInvalidControl" on the submit event or by any other way that you prefer.
SOLUTION # 2:
This is even simpler:
constructor(private elementRef: ElementRef) {
}
focusFirstInvalidControlPlus(): void {
const firstElementWithErrors: HTMLElement = this.elementRef.nativeElement.querySelector(`form .ng-invalid`);
if (firstElementWithErrors) {
firstElementWithErrors.focus();
}
}
Just trigger the method "focusFirstInvalidControlPlus" within your .ts file on the submit event. It works when using template driven and also with Reactive approach.
Upvotes: 1