[英]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.