0

I have read questions and answers here on StackOverflow about how to use "formatters" and "parsers" to allow the user to enter a value into an input, and then have that input transformed in the model.

I have a slightly different requirement.

I wish to both help and force the user to enter input in a certain format. In other words, both the display and the model should match exactly, but the user should be restricted to entering data in the specified format.

To use a common example, I want the user to enter a ten-digit phone number in the following format:

(xxx)xxx-xxxx

Coming from a jQuery background, I know that I can attach an event to the "change" event handler for the input and perform string manipulation to look for ten digits in the input and either format them as specified or show the user an error if there aren't exactly ten digits in the number.

I am also aware that I can use Angular to put restrictions on the input so that the form won't submit unless the format of the input matches the specification. This has the major disadvantage that the user may fill out the whole form that then be told to make a change to a previous field, breaking the user's workflow.

I want to go a step further and automatically format the number as soon as it is keyed in. What features of Angular are most appropriate for this task?

Use case: the user enters "9006345789". The code should, either as the user types the number, or as the user moves focus away from the input, replace "9006345789" with "(900)634-5789". The result should be the same if the user enters "900-634-5789" or "900.634.5789" or any other input with exactly ten digits.

Please bear in mind that the phone number example is one of many formatting examples and I would like my code to be reusable.

3
  • What about an input type number to force the user to enter only digit. And you could format it just when you send it to the server using a filter. Do you really need to reformat to show your expected format to the user ? Commented Sep 11, 2015 at 15:42
  • @Okazari, this is the way the requirement was given to me. Commented Sep 11, 2015 at 15:51
  • Do u want to change value on the fly or when input looses focus? Commented Sep 11, 2015 at 16:07

1 Answer 1

1

Actually, we can format the numbers with the use of Angularjs and i have attached the working link below.

  1. I created a directive which i used in the input field ( because we can use it in my places ).
  2. I created a Filter which will format the number the user type in the input field

var myApp = angular.module('myApp', []);

myApp.controller('MyCtrl', function($scope) {
  $scope.currencyVal;
}).directive('phoneInput', function($filter, $browser) {
  return {
    require: 'ngModel',
    link: function($scope, $element, $attrs, ngModelCtrl) {
      var 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);
      });

      ngModelCtrl.$render = function() {
        $element.val($filter('tel')(ngModelCtrl.$viewValue, false));
      };

      $element.bind('change', listener);
      $element.bind('keydown', function(event) {
        var key = event.keyCode;
        if (key == 91 || (15 < key && key < 19) || (37 <= key && key <= 40)) {
          return;
        }
        $browser.defer(listener);
      });

      $element.bind('paste cut', function() {
        $browser.defer(listener);
      });
    }

  };
});
myApp.filter('tel', function() {
  return function(tel) {
    console.log(tel);
    if (!tel) {
      return '';
    }

    var value = tel.toString().trim().replace(/^\+/, '');

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

    var country, 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 {
      return "(" + city;
    }

  };
});
<!DOCTYPE html>
<html>

<head>
  <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.5/angular.min.js"></script>
  <link rel="stylesheet" href="style.css">
  <script src="script.js"></script>
</head>

<body>
  <div class="body" ng-app="myApp">
    <div class="wrapper" ng-controller="MyCtrl">
      <div class="info">PHONE: {{phoneVal | tel}}</div>
      <input class="input-phone" type='text' phone-input ng-model="phoneVal" />
    </div>
  </div>
</body>

</html>

Hope it helps you :)

Sign up to request clarification or add additional context in comments.

2 Comments

This is similar to what I want, but there is a major difference. In my requirement, the display and the model should both read (900)6345789, but it appears that your code produces a model with 9006345789. I need both and model and the view to be (900)634-5789
I'm also running into a somewhat serious problem: If the user enters a digit in the middle of the phone number, the cursor jumps to the end of the input.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.