簡體   English   中英

cursor 問題在 ngModelChange Angular/Typescript 上結束

[英]Issue with cursor going to the end on ngModelChange Angular/Typescript

我的 HTML 輸入字段和使用 ngModelChange 的 typescript 組件有問題。 我希望能夠在任何需要的地方編輯輸入值。

例如:

  • 原始輸入自動填充為“00:00:00”。 我想將其編輯為“01:20:00”。
  • 用我的鍵盤我 position cursor (^) 我需要它到 0^0:00:00
  • 我輸入 1,結果是“01:00:00^”
  • 如果我想添加 2,我需要再次移動 cursor,這對我來說是不可取的。

我知道這是一個已知問題,可以通過使用 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 無關。

讓我們看看當您在輸入字段中鍵入內容時會發生什么。 這是一個非常明確的序列:

  1. ngModelChange觸發;
  2. formatAndChange運行並更新changedValue
  3. Angular 的變更檢測運行(此時formatAndChange方法已經完成);
  4. Angular 更新模板中的值,從而更新傳遞給ngModel的值;
  5. ngModel安排了一個微任務(我會在答案的最后解釋),它會更新實際的輸入元素值。

請注意,當ngModel更新時,甚至不會觸發ngModelChange

如果您嘗試在setSelectionRangeformatAndChange ,它永遠不會起作用,因為這是會發生的事情:

  1. changedValue被更新;
  2. cursor 被放置在輸入字段中預期的 position 處;
  3. 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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM