[英]Issue with cursor going to the end on ngModelChange Angular/Typescript
我的 HTML 输入字段和使用 ngModelChange 的 typescript 组件有问题。 我希望能够在任何需要的地方编辑输入值。
例如:
我知道这是一个已知问题,可以通过使用 setSelectionRange 重新设置 cursor 来解决,但是这没有用,因为即使我使用 setSelectionRange(selectionStart, selectionEnd) 和 cursor 的正确值,ngModelChange 也会把cursor 回到最后。
我还有一个正则表达式,它在每两位数字后应用冒号。
虽然这是我的代码,但我还提供了一个 stackblitz,您可以在其中使用它: https://stackblitz.com/edit/angular-ivy-adynjf?file=src/app/app.compone
这是我的输入字段:
<input
id="value"
type="text"
[ngModel]="changedValue"
(ngModelChange)="formatAndChange($event)"
/>
和我的一部分:
export class AppComponent {
public changedValue: String = "00:00:00";
public formatAndChange(inputValue: string) {
this.changedValue = inputValue;
if (inputValue.length > 8) {
inputValue = inputValue.substr(0, 8);
}
let unformat = inputValue.replace(/\D/g, "");
if (unformat.length > 0) {
inputValue = unformat.match(new RegExp(".{1,2}", "g")).join(":");
}
this.changedValue = new String(inputValue);
}
}
基本上我的问题是,如果我们想要它,应该如何使用这个结构:值在用户键入时更改并格式化(我们添加冒号以便格式正确),并且 cursor 保持不变(ngModelChange不会改变 cursor 的位置,或者至少我可以让它回到原来的位置)
欣赏它。 谢谢!!
这不太正确:
即使我使用 setSelectionRange(selectionStart, selectionEnd) 和 cursor 的正确值,ngModelChange 也会将 cursor 放回末尾。
每当值通过 JavaScript 更新时,浏览器会将 cursor 放在输入字段的末尾。与 Angular 无关。
让我们看看当您在输入字段中键入内容时会发生什么。 这是一个非常明确的序列:
ngModelChange
触发;formatAndChange
运行并更新changedValue
;formatAndChange
方法已经完成);ngModel
的值;ngModel
安排了一个微任务(我会在答案的最后解释),它会更新实际的输入元素值。 请注意,当ngModel
更新时,甚至不会触发ngModelChange
。
如果您尝试在setSelectionRange
中formatAndChange
,它永远不会起作用,因为这是会发生的事情:
changedValue
被更新;ngModel
和随后的输入值被更新,将 cursor 扔到输入的末尾。 为了使这个工作正常,您需要在输入值更新后调用setSelectionRange
- 所以至少在更改检测完成后调用微任务。 这是更新后的代码(请注意,由于数字之间有冒号,这并不完全正确,但我相信您可以自己弄清楚):
import {
AfterViewChecked,
Component,
ElementRef,
ViewChild
} from '@angular/core';
@Component({
selector: 'my-app',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent implements AfterViewChecked {
public changedValue: String = '00:00:00';
private valueUpdated: boolean;
private selectionStart: number;
private selectionEnd: number;
private selectionDirection: 'forward' | 'backward' | 'none';
@ViewChild('input')
private inputRef: ElementRef<HTMLInputElement>;
public formatAndChange(inputValue: string) {
console.log(inputValue);
const oldChangedValue = this.changedValue;
this.changedValue = inputValue;
if (inputValue.length > 8) {
inputValue = inputValue.substr(0, 8);
}
let unformat = inputValue.replace(/\D/g, '');
if (unformat.length > 0) {
inputValue = unformat.match(new RegExp('.{1,2}', 'g')).join(':');
}
console.log(inputValue);
this.changedValue = new String(inputValue);
this.valueUpdated = oldChangedValue !== this.changedValue;
if (this.valueUpdated && this.inputRef.nativeElement) {
const element = this.inputRef.nativeElement;
this.selectionStart = element.selectionStart;
this.selectionEnd = element.selectionEnd;
this.selectionDirection = element.selectionDirection;
}
}
// This lifecycle hook is called after change detection is complete for this component
ngAfterViewChecked() {
// This method is called VERY often, so we need to make sure that we only execute this logic when truly necessary (i.e. the value has actually changed)
if (this.valueUpdated && this.inputRef.nativeElement) {
this.valueUpdated = false;
// This is how you schedule a microtask
Promise.resolve().then(() => {
// Make sure you update this to deal with colons
this.inputRef.nativeElement.setSelectionRange(
this.selectionStart,
this.selectionEnd,
this.selectionDirection
);
});
}
}
}
微任务基本上是一些代码,在当前调用堆栈清空后执行。 Javascript 的任务和微任务是 JavaScript 引擎的核心,它们实际上并不那么容易掌握,但理解起来非常有用。
我不知道为什么 Angular 开发人员决定更新微任务中的输入值,一定有他们的原因。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.