问题描述
StackBlitz here. 请注意,addShortcut()
函数用于创建如下所述的热键。
我想在表单中动态移动,只关注无效字段,但允许用户根据需要遍历这些无效字段。
例如,如果我有一个表单,其中 address
、phone
和 email
输入是唯一的必填字段,则用户可以按下按钮并在这些特定字段之间循环.
现在,假设他们正在 3 个字段之间移动,并填写 address
字段。这不再无效,所以现在表单应该只循环/循环遍历 phone
和 email
输入字段。
我正在使用自定义热键功能,以便在用户按下 alt + a
时设置焦点。用户可以再次按下此按钮,焦点将转到下一个必填字段。
当应用运行时,ngOnInit()
构建表单,并触发一个函数来查找无效字段:
findInvalidFields() {
this.invalidFields = [];
const controls = this.myForm.controls;
for (const name in controls) {
if (controls[name].invalid) {
this.invalidFields.push(name);
}
}
}
this.invalidFields
现在是仅包含无效字段的数组:
[
"customerAddress","customerPhone","customerEmail"
]
按下热键,我运行一个函数来设置焦点,然后移动数组使其转到下一个无效项。:
... {
this.setFocus(this.invalidFields[0]);
this.invalidFields.push(this.invalidFields.shift());
}
现在的问题是,即使我填写了一个表单,焦点也会在整个原始列表中来回移动——这是意料之中的,因为无效字段仅在 OnInit 中被发现......当然,我可以运行findInvalidFields()
每次按下热键时,我的热键功能如下所示:
... {
this.findInvalidFields();
this.setFocus(this.invalidFields[0]);
this.invalidFields.push(this.invalidFields.shift());
}
这适用于获取正确的无效字段,但不再“循环”无效字段,因为表单每次运行函数时都会返回特定的控件顺序,因此会卡在输入上,直到它不再无效。
有没有办法动态“循环”输入字段而不会“卡住”无效字段?
解决方法
这是我的快速而肮脏的解决方案。它可能会被清理很多,但它似乎有效。
首先我们需要监听相关的按键并触发我们的函数来更新焦点,我使用了向右箭头键来简化它,
@HostListener('window:keyup',['$event'])
keyEvent(event: KeyboardEvent) {
if (event.key === 'ArrowRight') {
this.focusNext();
}
}
那么focusNext()函数就是这个样子,
private focusNext(): void {
let focusNextControl: boolean = false;
let focusSet: boolean = false;
let firstKey: string;
let currentControlInvalid: boolean;
Object.keys(this.myForm.controls).forEach((key,index) => {
currentControlInvalid = this.myForm.controls[key].invalid;
//Find the first invalid control,we will need it later in the case that the active focus is on the last control on the form
if(!firstKey && currentControlInvalid){
firstKey = key;
}
//If the current control is invalid
//AND the previous control is the currently focused one
//AND we haven't already set the focus.. Then set the focus on this control.
if(currentControlInvalid && focusNextControl && !focusSet){
this.elRef.nativeElement.querySelector('[formcontrolname = "' + key + '"]').focus();
focusSet = true;
}
else if(this.myForm.controls[key] == this.formFocus){
focusNextControl = true;
}
else if(!focusSet && focusNextControl && index == Object.keys(this.myForm.controls).length - 1){
//If we are on the last control and we need to set the 'next' control
//AND we haven't already focused on a control
//Then we need to set the focus to the first invalid control we found
this.elRef.nativeElement.querySelector('[formcontrolname = "' + firstKey + '"]').focus();
}
});
}
其中 this.formFocus 是我们用来跟踪主动聚焦表单控件的字段,
private formFocus: FormControl;
public onControlFocus(control: string): void {
this.formFocus = this.myForm.controls[control] as FormControl;
}
然后在您的模板中,您需要将每个输入的 (focus) 事件绑定到 onControlFocus()
并传入输入的 formcontrolname
,
<input
formControlName="firstName"
type="text" class="form-control" id="firstName"
(focus)="onControlFocus('firstName')"
>
这个解决方案的大部分内容都在 focusNext() 函数中,我存储第一个无效控件的方式有点混乱。如果你想清理它,你可以尝试将表单控件存储在一个循环链接列表中,这样你就可以直接迭代回第一个无效元素。这样你就可以避免一些跟踪变量。