Skip to content
Advertisement

How to create a directive in Angular to mask us phone number without changing model?

I have a directive in Angular Js that masks phone numbers to US format (xxx) xxx-xxxx ,

const myApp = angular.module("myApp", []);

myApp.controller("MyCtrl", function ($scope) {
  $scope.validateModel = function name(params) {
  };
});

// Excercise 1
myApp.directive("canadaPhone", function ($filter, $browser) {
  return {
    require: "ngModel",
    link: function ($scope, $element, $attrs, ngModelCtrl) {
      const listener = function () {
        var value = $element.val().replace(/[^0-9]/g, "");
        $element.val($filter("tel")(value, false));
      };

      // This runs when we update the text field
      ngModelCtrl.$parsers.push(function (viewValue) {
        return viewValue.replace(/[^0-9]/g, "").slice(0, 10);
      });

      // This runs when the model gets updated on the scope directly and keeps our view in sync
      ngModelCtrl.$render = () => {
        $element.val($filter("tel")(ngModelCtrl.$viewValue, false));
      };

      $element.bind("change", listener);
      $element.bind("keydown", (event) => {
        const key = event.keyCode;
        // If the keys include the CTRL, SHIFT, ALT, or META keys, or the arrow keys, do nothing.
        // This lets us support copy and paste too
        if (key == 91 || (15 < key && key < 19) || (37 <= key && key <= 40)) {
          return;
        }
        $browser.defer(listener); // Have to do this or changes don't get picked up properly
      });

      $element.bind("paste cut", () => {
        $browser.defer(listener);
      });
    },
  };
});
myApp.filter("tel", function () {
  return function (tel) {
    if (!tel) {
      return "";
    }

    let value = tel.toString().trim().replace(/^+/, "");

    if (value.match(/[^0-9]/)) {
      return tel;
    }

    let city, number;

    switch (value.length) {
      case 1:
      case 2:
      case 3:
        city = value;
        break;

      default:
        city = value.slice(0, 3);
        number = value.slice(3);
    }

    if (number) {
      if (number.length > 3) {
        number = number.slice(0, 3) + "-" + number.slice(3, 7);
      } else {
        number = number;
      }

      return ("(" + city + ") " + number).trim();
    } else {
      if (city) {
        return "(" + city;
      } else {
        return "";
      }
    }
  };
});

// Excercise 2
<!doctype html>
<html lang="en">

<head>
  <meta charset="utf-8">
  <title>SurexApp</title>
  <base href="/">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <link rel="icon" type="image/x-icon" href="favicon.ico">
    <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.9/angular.min.js"></script>
  <!-- CSS only -->
  <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.0-beta1/dist/css/bootstrap.min.css" rel="stylesheet"
    integrity="sha384-0evHe/X+R7YkIZDRvuzKMRqM+OrBnVFBL6DOitfPri4tjfHxaWutUpFmBp4vmVor" crossorigin="anonymous">


</head>

<body>

  <!-- Angular JS -->
  <div ng-app="myApp" ng-controller="MyCtrl">

    <div class="container app-container">
      <!-- <h1> Angular Js Excercises</h1> -->


      <!-- Excercise 1 -->
      <div>
        <label for="excercise1">Angular Js Excercise 1</label>
        <input type="text" ng-model="phone" id="excercise1" placeholder="(987) 654-3210" ng-change="validateModel()" canada-phone>
        <div> Model: {{phone}}</div>
      </div>


    </div>


  </div>

  <app-root></app-root>


</body>

</html>

How can I recreate the same directive in Angular ( typescript ) ?

I created a directive from another post but It alters the model too, My Requirement is that it should visually show as (xxx) xxx-xxxx, but the model value should not be changed i.e. it should be xxxxxxxxxx

My Stackblitz demo -> https://stackblitz.com/edit/angular6-phone-mask-7tglv9?file=app%2Fapp.component.html

Advertisement

Answer

Good comments under the question. If the end result is the need to mask the phone number, being able to change the number but still keep the unmasked number in the control, then I would recommend using a package for this. Complex template operations on input and form is no easy feat. https://www.npmjs.com/package/ngx-mask

Trick here is to use X with custom pattern. Idea from: https://github.com/JsDaddy/ngx-mask/issues/547#issuecomment-548596982

Html:

<div>
  TEL:
  <input
    matInput
    type="text"
    [(ngModel)]="tel.value"
    [hiddenInput]="true"
    mask="(000) 000-0000"
    [patterns]="pattern" 
  />
</div>

<div>TEL model: {{ tel.value }}</div>

TS:

@Component({
  selector: 'my-app',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css'],
})
export class AppComponent {
  pattern = { '0': { pattern: new RegExp('\d'), symbol: 'X' } };
  tel = new FormControl('4045912375');
}

enter image description here

Working example: https://stackblitz.com/edit/ngx-mask-xrpqce?file=src%2Fapp%2Fapp.component.html

User contributions licensed under: CC BY-SA
2 People found this is helpful
Advertisement