繁体   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