![](/img/trans.png)
[英]Angular 6 Reactive Forms : How to set focus on first invalid input
[英]Angular Custom focus Directive. Focus a form's first invalid input
如果輸入無效,我已經創建了一個指令來聚焦輸入
import { Directive, Input, Renderer2, ElementRef, OnChanges } from '@angular/core';
@Directive({
// tslint:disable-next-line:directive-selector
selector: '[focusOnError]'
})
export class HighlightDirective implements OnChanges {
@Input() submitted: string;
constructor(private renderer: Renderer2, private el: ElementRef) { }
ngOnChanges(): void {
const el = this.renderer.selectRootElement(this.el.nativeElement);
if (this.submitted && el && el.classList.contains('ng-invalid') && el.focus) {
setTimeout(() => el.focus());
}
}
}
我確實有一個帶有兩個輸入的反應形式,並且我已將指令應用於兩個輸入
<form>
...
<input type="text" id="familyName" focusOnError />
...
<input type="text" id="appointmentCode" focusOnError />
...
</form>
提交表單后它工作正常,但我努力實現的是以下內容:
預期結果: - 如果兩個輸入都無效,提交表單后,只應關注第一個。
當前結果: - 如果兩個輸入都無效,則提交表單后,第二個將獲得焦點。
我不知道如何指定“僅當它是第一個孩子時才這樣做”,我已經嘗試過使用指令的選擇器但沒有運氣。
有任何想法嗎?
非常感謝。
為了控制表單的輸入,我認為更好的解決方案是使用 ViewChildren 來獲取所有元素。 因此,我們可以遍歷這些元素並關注第一個元素。
所以,我們可以有一個輔助的簡單指令:
@Directive({
selector: '[focusOnError]'
})
export class FocusOnErrorDirective {
public get invalid()
{
return this.control?this.control.invalid:false;
}
public focus()
{
this.el.nativeElement.focus()
}
constructor(@Optional() private control: NgControl, private el: ElementRef) { }
}
而且,在我們的組件中,我們有一些像
@ViewChildren(FocusOnErrorDirective) fields:QueryList<FocusOnErrorDirective>
check() {
const fields=this.fields.toArray();
for (let field of fields)
{
if (field.invalid)
{
field.focus();
break;
}
}
}
你可以在stackblitz 中看到
更新總是可以改進的:
為什么不創建一個應用於表單的指令?
@Directive({
selector: '[focusOnError]'
})
export class FocusOnErrorDirective {
@ContentChildren(NgControl) fields: QueryList<NgControl>
@HostListener('submit')
check() {
const fields = this.fields.toArray();
for (let field of fields) {
if (field.invalid) {
(field.valueAccessor as any)._elementRef.nativeElement.focus();
break;
}
}
}
所以,我們的 .html 就像
<form [formGroup]="myForm" focusOnError>
<input type="text" formControlName="familyName" />
<input type="text" formControlName="appointmentCode" />
<button >click</button>
</form>
查看堆棧閃電戰
更重要的是,如果我們使用作為選擇器形式
@Directive({
selector: 'form'
})
甚至我們可以刪除表單中的 focusOnError
<form [formGroup]="myForm" (submit)="submit(myForm)">
..
</form>
更新 2 formGroup 與 formGroup 的問題。 解決了
NgControl 只考慮具有 [(ngModel)]、formControlName 和 [formControl] 的控件,所以。 如果我們可以使用像這樣的形式
myForm = new FormGroup({
familyName: new FormControl('', Validators.required),
appointmentCode: new FormControl('', Validators.required),
group: new FormGroup({
subfamilyName: new FormControl('', Validators.required),
subappointmentCode: new FormControl('', Validators.required)
})
})
我們可以使用如下形式:
<form [formGroup]="myForm" focusOnError (submit)="submit(myForm)">
<input type="text" formControlName="familyName" />
<input type="text" formControlName="appointmentCode" />
<div >
<input type="text" [formControl]="group.get('subfamilyName')" />
<input type="text" [formControl]="group.get('subappointmentCode')" />
</div>
<button >click</button>
</form>
我們在 .ts 中的位置
get group()
{
return this.myForm.get('group')
}
使用 Angular 8更新 3你可以獲得孩子的后代,所以它只是寫
@ContentChildren(NgControl,{descendants:true}) fields: QueryList<NgControl>
好吧,只是為了有趣的stackblitz 。 如果我們有一個 formControl,我們可以注入 ngControl,它是控件本身。 這樣我們就可以得到formGroup。 我控制“提交”在 app.component 中進行變通
<button (click)="check()">click</button>
check() {
this.submited = false;
setTimeout(() => {
this.submited = true;
})
}
該指令就像
export class FocusOnErrorDirective implements OnInit {
@HostListener('input')
onInput() {
this._submited = false;
}
//I used "set" to avoid ngChanges, but then I need the "ugly" work-around in app.component
@Input('focusOnError')
set submited(value) {
this._submited = value;
if (this._submited) { ((is submited is true
if (this.control && this.control.invalid) { //if the control is invalid
if (this.form) {
for (let key of this.keys) //I loop over all the
{ //controls ordered
if (this.form.get(key).invalid) { //If I find one invalid
if (key == this.control.name) { //If it's the own control
setTimeout(() => {
this.el.nativeElement.focus() //focus
});
}
break; //end of loop
}
}
}
else
this.el.nativeElement.focus()
}
}
}
private form: FormGroup;
private _submited: boolean;
private keys: string[];
constructor(@Optional() private control: NgControl, private el: ElementRef) { }
ngOnInit() {
//in this.form we has the formGroup.
this.form = this.control?this.control.control.parent as FormGroup:null;
//we need store the names of the control in an array "keys"
if (this.form)
this.keys = JSON.stringify(this.form.value)
.replace(/[&\/\\#+()$~%.'"*?<>{}]/g, '')
.split(',')
.map(x => x.split(':')[0]);
}
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.