ion-input: проверьте ввод на ionInput и установите фокус на следующее поле

#angular #ionic-framework #ionic4

#angular #ionic-framework #ionic4

Вопрос:

Я пытаюсь создать ионный (угловой) компонент для ввода вывода. Это будет 4 ion-input (по одному на каждое число), которые принимают только один символ и которые при включении ionInput (при нажатии клавиши клавиатуры) проверяют ввод и принимают (если это так [0-9] ) или отклоняют его. Если ввод принят, затем установите фокус на следующее поле; если он отклонен, затем удалите его и продолжайте фокусироваться на текущем поле. Ну, я не знаю почему, но мне это не удается. Поведение ion-input s не соответствует ожиданиям. В моем коде происходит то, что ввод отражается на следующем поле, а фокус устанавливается на поле после следующего. Например, я набираю a 1 в поле 0, затем я получаю a 1 в поле 0, другое 1 в поле 1 и фокус на поле 2 ; затем я набираю a 9 в поле 2 и получаю a 9 в 2, другое в 3 и фокус на 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();
        }

    }
  

Ответ №1:

Ну, я нашел способ сделать это.

Прежде чем читать код (который работает сейчас), я хочу кое-что прокомментировать: возможно, есть более простой способ, основанный на коде вопроса, поскольку в какой-то момент я понял, что использование digits массива в html-коде вызывает проблемы, даже когда я использую его только для *ngFor инициализации. Кроме того, preventDefault() stopImmediatePropagation() в какой-то момент помогло использование и.

В любом случае, когда мой код начал работать, я уже давно отказался от ionInput event, поэтому я использую keyDown вместо этого. Также keyDown дает мне возможность прослушать backspace.

Итак, нет более длинного введения, вот рабочий фрагмент:

 <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 недопустимый символ if. Я не знаю причину, но без нее это не сработает. Я предполагаю, что какой-то процесс работает в фоновом режиме в ionic и нарушает фокусировку и распространение значений. Я пробовал еще меньшее количество времени и все еще работает (оставил его на 10 мс для безопасности), но без него не будет работать.