ion-input:检查 ionInput 上的输入并将焦点设置在下一个字段

ion-input: check input on ionInput and set focus on next field

我正在尝试构建一个离子 (angular) 组件来输入 PIN。这将是 4 ion-input (每个数字一个)只接受一个字符,并且在 ionInput (当按下键盘的一个键时)检查输入并接受(如果它是 [0-9]) 或拒绝它。如果输入被接受,则将焦点设置到下一个字段;如果被拒绝,则将其删除并专注于当前字段。 好吧,我不知道为什么,但我没有成功。 ion-input 的行为不符合预期。 使用我的代码,发生的情况是输入回显到下一个字段并且焦点设置在下一个字段之后。例如,我在字段 0 上输入一个 1,然后在字段 0 中输入一个 1,在字段 1 中输入另一个 1,并且焦点在字段 2 上;然后我在字段 2 中输入一个 9,然后在字段 2 中输入一个 9,在字段 3 中输入一个 9,然后将焦点放在 1.

有人知道发生了什么事吗?而且,最重要的是,如何解决它? 我的代码如下:

<ion-input *ngFor="let digit of digits; index as i"
               [id]="'digitField'+i"
               [(ngModel)]="digit"
               inputmode="decimal"
               (ionInput)="onFieldInput($event, i)"></ion-input>
public ngAfterViewInit(): void {
        this.digitFields = [];
        for (let i = 0; i < this.digits.length; i++) {
            this.digitFields[i] = document.getElementById('digitField' + i) as HTMLIonInputElement;
        }
    }

public onFieldInput($event, fieldIndex: number) {
        const newInput = $event.detail.data;
        const nextIndex = (fieldIndex + 1) % this.digits.length;
        const currentElement = this.digitFields[fieldIndex];
        let nextElement = this.digitFields[nextIndex];

        if (!/\d/.test(newInput)) {
            this.digits[fieldIndex] = '';
            currentElement.setFocus();
        } else {
            this.digits[fieldIndex] = newInput;
            nextElement.setFocus();
        }

    }

好吧,我找到了方法。

在阅读代码(现在有效)之前,我想评论一下:也许有一种基于问题代码的更简单的方法,因为我在某个时候意识到使用 digits html 代码中的数组会引发问题,即使我只将它用于 *ngFor 初始化。此外,使用 preventDefault()stopImmediatePropagation() 在某些时候有所帮助。

无论如何,当我的代码开始工作时,我早就放弃了 ionInput 事件,这就是我改用 keyDown 的原因。另外 keyDown 让我有机会听退格键。

所以,不再介绍,这里是工作片段:

<div class="pin-input-container" #fieldsContainer>
    <ion-input *ngFor="let n of dummyArray; index as i"
               type="text"
               inputmode="numeric"
               [type]="hideInput ? 'password' : 'text'"
               (keydown)="onKeyDown($event, i)"></ion-input>
</div>
export class PinInputComponent {
    private static readonly TAG = 'PinInputComponent';

    @ViewChild('fieldsContainer') fieldsContainer;

    @Input() numberOfDigits = 4;
    @Input() hideInput = false;

    @Output() completed = new EventEmitter<string[]>();
    @Output() partial = new EventEmitter<string[]>();

    private digits: string[];
    public dummyArray: void[];

    public constructor() {
        this.dummyArray = new Array(this.numberOfDigits);
        this.digits = new Array(this.numberOfDigits);
    }

    public onKeyDown($event: KeyboardEvent, fieldIndex: number): void {
        $event.preventDefault();
        $event.stopImmediatePropagation();
        const domElement = $event.srcElement.parentElement as HTMLIonInputElement;

        if (/\d/.test($event.key)) {
            // Number: set field content, set digit in current position and go to next field
            domElement.value = $event.key;
            this.digits[fieldIndex] = $event.key;
            const nextField = domElement.nextElementSibling as HTMLIonInputElement;
            if (nextField) nextField.setFocus().then();

        } else if ($event.key === 'Backspace') {
            // Delete: clear content in field and this.digits and go to previous field
            domElement.value = null;
            this.digits[fieldIndex] = null;
            const previousField = domElement.previousElementSibling as HTMLIonInputElement;
            if (previousField) previousField.setFocus().then();

        } else {
            // Illegal key: clear field and stay there
            setTimeout(() => domElement.value = null, 10);
        }

        this.partial.emit(this.digits);

        // If the input was last digit and all digits are set, then emit completed
        if (fieldIndex >= this.digits.length - 1) {
            for (const digit of this.digits) {
                if (digit === null) {
                    return;
                }
            }

            this.completed.emit(this.digits);
        }
    }

    public setFocus(): void {
        (this.fieldsContainer.nativeElement.childNodes[1] as HTMLIonInputElement).setFocus().then();
    }
}

请注意,如果是非法字符,我使用了 setTimeout。我不知道为什么,但没有它就不行。我猜想某些进程在 ionic 的后台运行并扰乱了焦点和价值传播。我已经尝试了更短的时间并且仍然有效(为了安全起见将其保留为 10 毫秒),但没有它就无法工作。