Search code examples
angularreplaceposition

Using replace and position setters not working properly


I have following code, idea is to change input value to different language and show it in the input. It works fine, but as soon an you place cursor in the middle of the text for example and type something there cusror jumps to the end again.

So far I have this:

<mat-form-field *ngIf="operationType != 'joint'"
[ngClass]="{'inputBIG uniqueStander':item?.person?.factualAddress?.length > 50, 'inputBIG':!item?.person?.factualAddress?.length > 50}"
class="inputBIG" appearance="outline">
<mat-label>მისამართი</mat-label>
<input autocomplete="off" autocomplete="off" matInput placeholder=""
  [(ngModel)]="dataaa" name="address" [maxlength]="maxFactAddress"
  (keyup)="iReplaceToGEO('firstAddr')" id="firstAddr">
</mat-form-field>

and typescript as well:

  dataaa: any;

  public iReplaceToGEO(position?): void {
    console.log('position', position);
    if (position) {
      this.getCaretPosition(position);
    }
    this.dataaa = replacer(this.dataaa);
    if (position) {
      this.setCaretPosition(position, this.caretPos, this.caretPos);
    }
    function replacer(val): string {
      val = val
        .replace(/a/g, 'ა')
        .replace(/b/g, 'ბ')
        .replace(/c/g, 'ც')
        .replace(/d/g, 'დ')
        .replace(/e/g, 'ე')
        .replace(/f/g, 'ფ')
        .replace(/g/g, 'გ')
        .replace(/h/g, 'ჰ')
        .replace(/i/g, 'ი')
        .replace(/j/g, 'ჯ')
        .replace(/k/g, 'კ')
        .replace(/l/g, 'ლ')
        .replace(/m/g, 'მ')
        .replace(/n/g, 'ნ')
        .replace(/o/g, 'ო')
        .replace(/p/g, 'პ')
        .replace(/q/g, 'ქ')
        .replace(/r/g, 'რ')
        .replace(/s/g, 'ს')
        .replace(/t/g, 'ტ')
        .replace(/u/g, 'უ')
        .replace(/v/g, 'ვ')
        .replace(/w/g, 'წ')
        .replace(/x/g, 'ხ')
        .replace(/y/g, 'ყ')
        .replace(/z/g, 'ზ')
        .replace(/J/g, 'ჟ')
        .replace(/T/g, 'თ')
        .replace(/R/g, 'ღ')
        .replace(/S/g, 'შ')
        .replace(/C/g, 'ჩ')
        .replace(/Z/g, 'ძ')
        .replace(/W/g, 'ჭ');
      return val;
    }
  }

  caretPos: number = 0;
  public getCaretPosition(ctrl) {
    let arr: any = document.getElementById(ctrl);
    console.log('getCaretPosition', arr.selectionStart);
    if (arr.selectionStart || arr.selectionStart == '0') {
      this.caretPos = arr.selectionStart;
      return {
        start: arr.selectionStart,
        end: arr.selectionEnd,
      };
    } else {
      this.caretPos = 0;
      return {
        start: 0,
        end: 0,
      };
    }
  }
  public setCaretPosition(ctrl, start, end) {
    let arr: any = document.getElementById(ctrl);
    console.log('setCaretPosition', arr.setSelectionRange);
    console.log('start', start);
    console.log('end', end);
    if (arr.setSelectionRange) {
      arr.focus();
      arr.setSelectionRange(start, end);
    } else if (arr.createTextRange) {
      var range = arr.createTextRange();
      range.collapse(true);
      range.moveEnd('character', end);
      range.moveStart('character', start);
      range.select();
    }
    this.caretPos = this.caretPos + 1;
  }

You can also check stackblitz example: https://stackblitz.com/edit/angular-material-mat-form-fields-kpryeu?file=src%2Fapp%2Fapp.component.html,src%2Fapp%2Fapp.component.ts


Solution

  • It's because yo need "wait" to Angular redraw the value before position the cursor.

    I simply a bit your code

    public iReplaceToGEO(event:any,name:string): void {
        const item=event.target;
        const pos = item.selectionStart;
        const value = item.value;
        this[name]=this.replacer(value);
        setTimeout(()=>{
          item.selectionStart=item.selectionEnd=pos;
        })
      }
    

    Where you define your function replacer outside the function:

     replacer(val:string): string {
        return val
          .replace(/a/g, 'ა')
          .replace(/b/g, 'ბ')
          ...
     }
    

    You use not using (keyup) else (input) event

    <input matInput  ... 
           [(ngModel)]="dataaa" 
           (input)="iReplaceToGEO($event,'dataaa')" >
    

    Your forked stackblitz

    NOTE: If you use Reactive Forms you should use form.get(name).setValue(this.replacer(value)) instead of this[name]=this.replacer(value);

    NOTE2: using (input) work also CTRL+V