Javascript 光标在ngModelChange Angular/Typescript上结束时出现问题

Javascript 光标在ngModelChange Angular/Typescript上结束时出现问题,javascript,angular,typescript,ngmodel,Javascript,Angular,Typescript,Ngmodel,我的HTML输入字段和使用ngModelChange的typescript组件有问题。我希望能够在任何需要的地方编辑输入值 例如: 原始输入自动填充为“00:00:00”。我想把它编辑成“01:20:00” 我用键盘将光标(^)定位到需要的位置,使其处于0^0:00:00 我输入1,结果是“01:00:00^” 如果我想添加2,我需要再次移动光标,这对我来说是不可取的 我知道这是一个已知的问题,可以通过使用setSelectionRange重新设置光标来解决,但是这不起作用,因为即使我使用s

我的HTML输入字段和使用ngModelChange的typescript组件有问题。我希望能够在任何需要的地方编辑输入值

例如:

  • 原始输入自动填充为“00:00:00”。我想把它编辑成“01:20:00”
  • 我用键盘将光标(^)定位到需要的位置,使其处于0^0:00:00
  • 我输入1,结果是“01:00:00^”
  • 如果我想添加2,我需要再次移动光标,这对我来说是不可取的
我知道这是一个已知的问题,可以通过使用setSelectionRange重新设置光标来解决,但是这不起作用,因为即使我使用setSelectionRange(selectionStart,selectionEnd)和正确的光标值,ngModelChange也会将光标放回末尾

我还有一个正则表达式,它在每两个数字后应用冒号

虽然这是我的代码,但我还提供了一个stackblitz,您可以在其中使用它:

这是我的输入字段:

<input
  id="value"
  type="text"
  [ngModel]="changedValue"
  (ngModelChange)="formatAndChange($event)"
/>
基本上,我的问题是,如果我们想全部使用这个结构,那么应该如何使用它:值在用户键入时更改并格式化(我们添加冒号以便格式正确),光标保持在原位(ngModelChange不会更改光标的位置,或者至少我可以让它返回到原来的位置)

谢谢。
谢谢

这不太正确:

即使我使用setSelectionRange(selectionStart,selectionEnd)和光标的正确值,ngModelChange也会将光标放回末尾

只要值通过JavaScript更新,浏览器就会将光标放在输入字段的末尾。与棱角无关

让我们看看在输入字段中键入内容时会发生什么。这是一个非常明确的序列:

  • ngModelChange
    fires
  • formattandchange
    运行并更新
    changedValue
  • Angular的更改检测运行(此时,
    formatAndChange
    方法已完成)
  • Angular更新模板中的值,从而更新传递给
    ngModel
    的值
  • ngModel
    调度一个微任务(我将在回答的末尾解释),它更新实际的输入元素值
  • 请注意,当更新
    ngModel
    时,
    ngModelChange
    甚至不会触发

    如果您试图在
    格式和更改
    设置SelectionRange
    ,它永远不会起作用,因为会发生以下情况:

  • changedValue
    更新
  • 光标放置在输入字段中的预期位置
  • ngModel
    ,然后更新输入值,将光标移到输入的末尾
  • 要实现这一点,您需要在更新输入值后调用
    setSelectionRange
    ,这样至少可以在完成更改检测后调用微任务。以下是更新后的代码(请注意,由于数字之间有冒号,这并不完全正确,但我相信您可以自己解决):

    导入{
    检查后,
    组成部分,
    ElementRef,
    视图儿童
    }从“@angular/core”开始;
    @组成部分({
    选择器:“我的应用程序”,
    templateUrl:“./app.component.html”,
    样式URL:['./app.component.css']
    })
    导出类AppComponent实现AfterViewChecked{
    publicchangedvalue:String='00:00:00';
    私有值更新:布尔值;
    私人选择开始:号码;
    私有选择结束:数字;
    私有选择方向:“向前”|“向后”|“无”;
    @ViewChild('输入')
    私有inputRef:ElementRef;
    公共formatAndChange(inputValue:string){
    console.log(输入值);
    const oldChangedValue=this.changedValue;
    this.changedValue=输入值;
    如果(inputValue.length>8){
    inputValue=inputValue.substr(0,8);
    }
    设unformat=inputValue.replace(/\D/g');
    如果(未格式化长度>0){
    inputValue=unformat.match(新的RegExp('.{1,2}',g')).join(':');
    }
    console.log(输入值);
    this.changedValue=新字符串(inputValue);
    this.valueUpdated=oldChangedValue!==this.changedValue;
    if(this.valueUpdated&&this.inputRef.nativeElement){
    常量元素=this.inputRef.nativeElement;
    this.selectionStart=element.selectionStart;
    this.selectionEnd=element.selectionEnd;
    this.selectionDirection=element.selectionDirection;
    }
    }
    //此组件的更改检测完成后,将调用此生命周期挂钩
    ngAfterViewChecked(){
    //这个方法经常被调用,所以我们需要确保我们只在真正需要的时候执行这个逻辑(即值实际上已经改变)
    if(this.valueUpdated&&this.inputRef.nativeElement){
    this.valueUpdated=false;
    //这就是安排微任务的方式
    Promise.resolve()然后(()=>{
    //请确保更新此文件以处理冒号
    this.inputRef.nativeElement.setSelectionRange(
    这是selectionStart,
    这个.selectionEnd,
    这是选择方向
    );
    });
    }
    }
    }
    
    微任务 微任务基本上是一些代码,在当前调用堆栈清空后执行。Javascript的任务和微任务是Javascript引擎的核心,它们实际上不是那么容易掌握,但是理解起来非常有用


    我不知道为什么Angular开发人员决定更新微任务中的输入值,这肯定有他们的原因。

    如果你没有太多Angular和/或JS的经验,这可能看起来太多了。Angular是一个复杂的框架,您可能需要阅读一些关于变更检测和组件生命周期的内容,以便更好地理解它。
    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);
      }
    }