Angular 8自定义OTP输入组件:值重复与退格行为异常问题
自定义Angular 8 OTP输入组件问题排查
我了解已有用于OTP输入框的npm包,但为了更好的定制性与学习需求,从零构建了Angular 8的6位OTP输入组件。该组件通过*ngFor动态生成输入框,使用[value]绑定与事件监听维护状态,但遇到两个问题:
- 值重复问题:在第一个输入框(索引0)输入数字时,第二个输入框(索引1)会出现相同数字;
- 退格行为异常:点击第二个输入框并按下退格键时,删除的是第一个输入框的值而非当前框的值。
组件代码
HTML(otp-input.component.html)
<div class="otp-container"> <input *ngFor="let digit of otpArray; let i = index" type="text" class="otp-input" maxlength="1" [value]="otpArray[i]" (input)="onInput($event, i)" (keydown)="onKeyDown($event, i)" #otpInput /> </div>
TypeScript(otp-input.component.ts)
import { Component, EventEmitter, Output, ViewChildren, ElementRef, QueryList } from '@angular/core'; @Component({ selector: 'app-otp-input', templateUrl: './otp-input.component.html', styleUrls: ['./otp-input.component.css'] }) export class OtpInputComponent { otpLength = 6; otpArray: string[] = new Array(this.otpLength).fill(''); @Output() otpCompleted = new EventEmitter<string>(); @ViewChildren('otpInput') otpInputs!: QueryList<ElementRef>; onInput(event: Event, index: number): void { const inputElement = event.target as HTMLInputElement; const value = inputElement.value; // Ensure the correct value is assigned to the correct index this.otpArray[index] = value; console.log(`User entered value \"${this.otpArray[index]}\" at index ${index}`); const inputEvent = event as InputEvent; if (inputEvent.inputType === 'deleteContentBackward') { console.log('User pressed delete on input ' + index); return; } if (value && index < this.otpLength - 1) { this.otpInputs.toArray()[index + 1].nativeElement.focus(); } this.checkOtpCompletion(); } onKeyDown(event: KeyboardEvent, index: number): void { if (event.key === 'Backspace') { if (this.otpArray[index]) { this.otpArray[index] = ''; // Clear current input } else if (index > 0) { console.log('Backspace pressed, moving to previous index:', index); this.otpInputs.toArray()[index - 1].nativeElement.focus(); } } } checkOtpCompletion(): void { const otpValue: string = this.otpArray.join(''); if (otpValue.length === this.otpLength) { this.otpCompleted.emit(otpValue); } } }
预期行为
- 每个数字仅输入到选中的输入框,不影响其他框;
- 按下退格键时先清空当前框,再将焦点移至上一个输入框。
已尝试的解决方法
- 将
[value]替换为[(ngModel)]; - 检查意外的变更检测更新;
- 添加控制台日志验证更新顺序;
- 在
onInput函数中手动处理状态更新。
问题
- 输入时为何值会在下一个输入框重复?
- 如何避免退格键先删除上一个框的值而非当前框?
问题分析与解决方案
1. 值重复问题原因及修复
原因:使用new Array(this.otpLength).fill('')初始化数组时,所有元素指向同一个空字符串引用。当修改otpArray[index]时,Angular的变更检测会错误地更新绑定了相同引用的其他输入框,导致值重复。
修复:初始化数组时确保每个元素都是独立的空字符串:
otpArray: string[] = Array.from({ length: this.otpLength }, () => '');
同时,在onInput方法中过滤非数字输入并阻止默认行为,避免重复处理:
onInput(event: Event, index: number): void { const inputElement = event.target as HTMLInputElement; // 只保留数字字符 const value = inputElement.value.replace(/[^0-9]/g, ''); event.preventDefault(); this.otpArray[index] = value; if (value && index < this.otpLength - 1) { setTimeout(() => { this.otpInputs.toArray()[index + 1].nativeElement.focus(); }, 0); } this.checkOtpCompletion(); }
2. 退格行为异常原因及修复
原因:keydown事件触发后,默认的退格行为会触发input事件,而onInput中对deleteContentBackward的判断会直接返回,导致焦点逻辑未执行;同时状态更新与焦点移动的时机不同步。
修复:在keydown中完整处理退格逻辑,并阻止默认行为避免input事件重复处理:
onKeyDown(event: KeyboardEvent, index: number): void { if (event.key === 'Backspace') { event.preventDefault(); if (this.otpArray[index]) { this.otpArray[index] = ''; } else if (index > 0) { const prevInput = this.otpInputs.toArray()[index - 1].nativeElement; prevInput.focus(); // 若需要清空上一个输入框,可取消注释下面一行 // this.otpArray[index - 1] = ''; } this.checkOtpCompletion(); } }
同时移除onInput方法中对deleteContentBackward的判断逻辑。
内容的提问来源于stack exchange,提问作者Yoni Shkolsky
相关产品推荐
相关产品推荐

