angularuser-interfaceinputformatting

Angular - Updating an Input-Field without losing the cursor position


I'm having a real struggle to solve this behavior and hope you guys/girls can help me out. I checked out multiple websites and questions on this platform, but I wasn't able to figure it out.

What I would like to do:

For my projects I wanna create my own ui-components. One of those is a number input, which allows decimal numbers, and display them nicely with thousand and decimal separators. The formatting should occur on every change while the user is focusing in the element and not, as other solutions out there, after the element blurred out.

My Problem:

The formatting of a value to the visual appealing string is not a problem, for that I just use the decimal pipe. But when I change the value of the input-field, through one-way binding, the user-cursor moves to the end of the value. Is there a way to stop that behavior and keep the cursors position?

Simplified Version of the Problem

Enter some numbers into the input-field and they will automatically change into the en-US number format. Then move the cursor to somewhere to the left and enter a new number. The input field will update correctly, but also move the cursor to the end of the value.

https://stackblitz.com/edit/angular-input-field-example?file=src/app/app.component.ts

EDIT: Solution with the help of @Eliseo

The user @Eliseo answered this question and sent a link to another question on Stackoverflow. With the solution provided there, I was able to create an input field which automatically formats my inputs immediately but also keeps the cursor in the position as nothing has happened. The magic happens in the directive mask.directive.ts

https://stackblitz.com/edit/angular-input-field-solution?file=src/app/app.component.ts


Solution

  • you need "play" with the properties selectionStart and selectionEnd of the own input, you can see an example in this SO (The question use a directive, it's only to help you to get the idea)

    Simplifying so much, if you pass the own input (you can also use viewChild and ask about this.yourvariable.nativeElement)

    <input #myinput type="text" class="form-control"
               [value]="value | number: '.0' : 'en-US'"
               (input)="onInput($event,myinput)">
    

    Some like

    onInput(event: Event,inputhtml:any): void {
        //get the actual position of the cursor
        let pos=inputhtml.selectionStart;
        
        //get the characteres not digit before change:
        const nondigits=...
    
        //change the value
        this.value = +(<HTMLInputElement>event.target).value.split(',').join('');
    
        //recalcule the position, e.g.
         pos+=nondigits;
    
        //in a setTimeout (sometime this can be not neccesary)
        setTimeout(()=>{
           //use selectionStart and selectionEnd to position the cursor
           inputhtml.selectionStart=inputhtml.selectionEnd=pos
    
        })
      }