3

I have refered the link: Input mask fields in Angular2 forms and I was able to mask input field like: (123) 456-7890.

Please find updated code below:

Directive TS File:

@Directive({
  selector: '[appPhoneMasking]',
  host: {
    '(ngModelChange)': 'onInputChange($event)',
    '(keydown.backspace)': 'onInputChange($event.target.value, true)'
  }
})
export class PhoneMaskingDirective {

  constructor(public model: NgControl) {}

  onInputChange(event, backspace) {
    // remove all mask characters (keep only numeric)
    var newVal = event.replace(/\D/g, '');

    if (backspace) {
      newVal = newVal.substring(0, newVal.length - 1);
    }

    // don't show braces for empty value
    if (newVal.length == 0) {
      newVal = '';
    }

    // don't show braces for empty groups at the end
    else if (newVal.length <= 3) {
      newVal = newVal.replace(/^(\d{0,3})/, '($1)');
    } else if (newVal.length <= 6) {
      newVal = newVal.replace(/^(\d{0,3})(\d{0,3})/, '($1) $2-');
    } else {
      newVal = newVal.replace(/^(\d{0,3})(\d{0,3})(.*)/, '($1) $2-$3');
    }

    if(newVal.length > 14) {
       newVal = newVal.slice(0, 14); 
    }
    // set the new value
    this.model.valueAccessor.writeValue(newVal);       
  }
}

And HTML file:

<form [formGroup]="addUser" (ngSubmit)="submitUser()">
     <input type="text" class="form-control" formControlName="user_phone"  appPhoneMasking>
</form>

Component File:

this.addUser= this._formBuilder.group({
   user_phone: new FormControl('', [Validators.maxLength(14)]),
});

I have two issue which I am trying to solve but no success. These are:

  1. When I press backspace two character are deleted from the string.
  2. Also I want only 14 characters to be in phone number. As soon as I type 15 character the input field is updated. But the form is not updated. I get error that my form is not valid.

Please let me know how can I fix these issues.

Tavish Aggarwal
  • 1,020
  • 3
  • 22
  • 51

3 Answers3

1

When I press backspace two character are deleted from the string.

you don't need the if for the backspace, the backspace event is working, and after that you are removing one more character.

0

Change your if (backspace) condition slightly to:

if (backspace && (event[event.length - 1] == "-" || event[event.length - 1] == ")")) {...}

Also, make sure the event parameter is set to type string to access the length property.

Without the added condition, backspace would always delete 2 characters. However, when you remove the if statement altogether. It would get stuck whenever you reached a dash or closing parenthesis. This is because the default backspace event would remove that formatting character but then it'd get added back in at the end due to the formatting behavior of your function.

UPDATE: If you want your cursor position to persist instead of jumping to the end. You just need to add a private ElementRef property in the constructor, and tweak a few things at the start of your function:

  constructor(
    private el: ElementRef,
    public model: NgControl,
  ) { }

  onInputChange(event: string, backspace) {
    // remove all mask characters (keep only numeric)
    var newVal = event.replace(/\D/g, '');

    let start = this.el.nativeElement.selectionStart;

    if (backspace && (event[event.length - 1] == "-" || event[event.length - 1] == ")")) {
      if (start == event.length) {
        newVal = newVal.substring(0, newVal.length - 1);
      }
      else {
        setTimeout(() => {
          this.el.nativeElement.selectionStart = start - 1;
          this.el.nativeElement.selectionEnd = start - 1;
        });
      }
    }
    else {
      if (backspace) {
        setTimeout(() => {
          this.el.nativeElement.selectionStart = start - 1;
          this.el.nativeElement.selectionEnd = start - 1;
        });
      }
      else {
        if (start != event.length) {
          switch (start) {
            case 5:
              setTimeout(() => {
                this.el.nativeElement.selectionStart = start + 2;
                this.el.nativeElement.selectionEnd = start + 2;
              });
              break;
            case 6:
              setTimeout(() => {
                this.el.nativeElement.selectionStart = start + 1;
                this.el.nativeElement.selectionEnd = start + 1;
              });
              break;
            case 10:
              setTimeout(() => {
                this.el.nativeElement.selectionStart = start + 1;
                this.el.nativeElement.selectionEnd = start + 1;
              });
              break;
            default:
              setTimeout(() => {
                this.el.nativeElement.selectionStart = start;
                this.el.nativeElement.selectionEnd = start;
              });
              break;
          }
        }
      }
    }
....
}

Note: It's important that you set the cursor position within a setTimeout() in order for it to actually set.

Luke Weaver
  • 271
  • 1
  • 9
0
   <div class="row">
        <div class="col s4">
          <mat-form-field appearance="outline" class="common-form-field-width">
            <mat-label>{{'MOBILE' | translate: lang}}</mat-label>
            <input appTrim required matInput [placeholder]="patternDetails?.phoneNumberFormat" autocomplete="off" formControlName="userMobile">
            <mat-hint *ngIf="(CustomerForm.controls['userMobile'].invalid && (CustomerForm.controls['userMobile'].dirty || CustomerForm.controls['userMobile'].touched))">
              <mat-error *ngIf="CustomerForm.controls['userMobile'].errors.required">
                {{ 'REQUIRED' | translate:lang }}
              </mat-error>
              <mat-error *ngIf="CustomerForm.controls['userMobile'].errors.pattern">
                {{ 'ERROR_INVALID_MOBILE' | translate:lang }} {{'LABEL_EX' | translate: lang}} {{patternDetails?.phoneNumberFormat}})
              </mat-error>
              <mat-error *ngIf="CustomerForm.controls['userMobile'].errors.minlength">
                {{ 'ERROR_MOBILE_DIGITS' | translate:lang }}{{customerRules.MOBILE_MIN_LENGTH}}{{ 'DIGITS' | translate:lang}}
              </mat-error>
            </mat-hint>
          </mat-form-field>
        </div>
userMobile: ['', [Validators.required, Validators.pattern(this.customerRules.MOBILE_PATTERN),
                        Validators.minLength(this.customerRules.MOBILE_MIN_LENGTH)]]

export const CUSTOMER_RULE = () => {
  return {
  'NAME_PATTERN': `[${getUniCode()}]+(\\s[${getUniCode()}]+){0,}?`,
  'FIRST_NAME': `[${getUniCode()}0-9]+(\\s[${getUniCode()}0-9]+){0,}?`,
  'FIRST_NAME_MIN_LENGTH': 3,
  'FIRST_NAME_MAX_LENGTH': 100,
  'LAST_NAME': `[${getUniCode()}0-9]+(\\s[${getUniCode()}0-9]+){0,}?`,
  'LAST_NAME_MIN_LENGTH': 1,
  'LAST_NAME_MAX_LENGTH': 100,
  'MOBILE_PATTERN': '[+0-9]*',
  'MOBILE_MIN_LENGTH': 4,
  'MOBILE_MAX_LENGTH': 13,
  'ADDRESS_MIN_LENGTH': 3,
  'ADDRESS_MAX_LENGTH': 250,
  'ZIPCODE': '[0-9]*',
  'ZIPCODE_MIN_LENGTH': 4,
  'ZIPCODE_MAX_LENGTH': 10,
  'PASSWORD_PATTERN': '^(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])(?=.*[`~#^()_+={}<>";:|[/,.$@$!%*?&-])[A-Za-z0-9`~#^()_+={}<>":;|[/,.$@$!%*?&-]{6,20}',
  'EMAIL_PATTERN': '^[_A-Za-z0-9-\\+]+(\\.[_A-Za-z0-9-]+)*@[A-Za-z0-9-]+(\\.[A-Za-z0-9]+)*(\\.[A-Za-z]{2,})$',
  'RESOLUTION': {
    'RESOLUTION_WIDTH': 0,
    'RESOLUTION_HEIGHT': 0
   }
  };
};

enter image description here

enter image description here

enter image description here

Darshan Malani
  • 478
  • 3
  • 12