Question

I was wondering how to automatically format a number in an input field using an angularjs directive? When I type in an input field say 6042919283 I want it to be shown as 604-291-9283.

Thanks

Was it helpful?

Solution

You could use UI Utils mask It allows you to define an allowd input format and will handle the validation for you as well as the formatting

OTHER TIPS

If your phone number is uniform i.e all the number is of digit 10 this one will work

  app.directive('formatPhone', [
        function() {
            return {
                require: 'ngModel',
                restrict: 'A',
                link: function(scope, elem, attrs, ctrl, ngModel) {
                    elem.add(phonenumber).on('keyup', function() {
                       var origVal = elem.val().replace(/[^\w\s]/gi, '');
                       if(origVal.length === 10) {
                         var str = origVal.replace(/(.{3})/g,"$1-");
                         var phone = str.slice(0, -2) + str.slice(-1);
                         jQuery("#phonenumber").val(phone);
                       }

                    });
                }
            };
        }
    ]);

And your html;

<input type="text" id="phonenumber" ng-model="phonenumber" format-phone>

Here is how I did it with a custom directive.

customDirective.js

demoApp.directive('phoneInput', [ '$filter', '$browser', 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);
            });

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

            $element.bind('change', listener);
            $element.bind('keydown', function(event) {
                var 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', function() {
                $browser.defer(listener);
            });
        }

    };
}]);

And using this custom filter you can filter the model.

customFilter.js

demoApp.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;
        }

    };
});

HTML

    <input type = "text" id="phonenumber" phone-input ng-model="USPhone" >
    <p>{{USPhone | tel}}</p>

I've used the ui-mask directive in AngularUI to mask fields in the past with great success. The documentation isn't very helpful, but here's a simple example of how to make it work.

Plunkr

  • Angular@1.4.1
  • Angular-UI-Utils@0.1.1

I wrote this, and works pretty well. The only catch is you cannot delete dashes "-" in the number. This code can be easily modified to account for that.

In addition, I have a validator, invalidFormat that a user can set a custom message in case the phone number is invalid

app.directive("phoneNumberValidator", function () {
    return {
        require: "ngModel",
        restrict: "A",
        link: function (scope, elem, attrs, ctrl) {

            var domElement = elem[0]; // Get DOM element
            var phoneNumberRegex = new RegExp("\\d{3}\\-\\d{3}\\-\\d{4}"); // Phone number regex
            var cursorIndex; // Index where the cursor should be

            // Create a parser to alter and validate if our
            // value is a valid phone number
            ctrl.$parsers.push(function (value) {

                // If our value is non-existent, we return undefined
                // WHY?: an angular model value should be undefined if it is empty
                if (typeof value === "undefined" || value === null || value == "") {
                    ctrl.$setValidity('invalidFormat', true); // No invalid format if the value of the phone number is empty
                    return undefined;
                }

                // PARSER LOGIC
                // =compare our value to a modified value after it has
                // been transformed into a "nice" phone number. If these
                // values are different, we set the viewValue to
                // the "nice" phone number. If these values are the same,
                // we render the viewValue (aka. "nice" phone number)
                var prevValue, nextValue;

                prevValue = value;
                nextValue = value.replace(/[\D]/gi, ""); // Strip all non-digits

                // Make the "nice" phone number
                if (nextValue.length >= 4 && nextValue.length <= 6) {
                    nextValue = nextValue.replace(/(\d{3})(\d{3})?/, "$1-$2");
                } else if (nextValue.length >= 7 && nextValue.length <= 10) {
                    nextValue = nextValue.replace(/(\d{3})(\d{3})(\d{4})?/, "$1-$2-$3");
                }

                // Save the correct index where the custor should be
                // WHY?: we do this here because "ctrl.$render()" shifts
                // the cursor index to the end of the phone number
                cursorIndex = domElement.selectionStart;
                if (prevValue != nextValue) {
                    ctrl.$setViewValue(nextValue); // *Calling this function will run all functions in ctrl.$parsers!
                } else {
                     ctrl.$render(); // Render the new, "nice" phone number
                }

                // If our cursor lands on an index where a dash "-" is,
                // move it up by one
                if (cursorIndex == 4 || cursorIndex == 8) {
                    cursorIndex = cursorIndex + 1;
                }

                var valid = phoneNumberRegex.test(value); // Test the validity of our phone number
                ctrl.$setValidity('invalidFormat', valid); // Set the validity of the phone number field
                domElement.setSelectionRange(cursorIndex, cursorIndex); // Assign the cursor to the correct index

                return value; // Return the updated value
            });
        }
    }
});

The best place to put validators is in $parsers, which I found my answer from evidence found here: http://radify.io/blog/understanding-ngmodelcontroller-by-example-part-1/. This is why my answer is a bit different than the others.

In the HTML

<input type="tel" class="form-control" id="PhoneNumber" name="PhoneNumber" ng-model="PhoneNumber" placeholder="Phone" maxlength="12" ng-value="PhoneNumber" required phone-number-validator server-validation>
<p class="help-block" ng-if="PhoneNumber.$error.invalidFormat">Phone Number is invalid</p>

Custom directive for telephone format using angularjs

The field change format as user typing

Restricts the input to numbers only

Auto formats the input (541) 754-3010

app.directive("phoneNumberValidator", function () {
        return {	
            restrict: 'A',
			link: function (scope, elem, attrs, ctrl, ngModel) {
				elem.add(phonenumber).on('keyup', function () {
					var input = elem.val();
					// Strip all characters from the input except digits
					input = input.replace(/\D/g, '');

					// Trim the remaining input to ten characters, to preserve phone number format
					input = input.substring(0, 10);

					// Based upon the length of the string, we add formatting as necessary
					var size = input.length;
					if (size == 0) {
						input = input;
					} else if (size < 4) {
						input = '(' + input;
					} else if (size < 7) {
						input = '(' + input.substring(0, 3) + ') ' + input.substring(3, 6);
					} else {
						input = '(' + input.substring(0, 3) + ') ' + input.substring(3, 6) + ' - ' + input.substring(6, 10);
					}
					jQuery("#phonenumber").val(input);
				});
			}
		}
    });

code form [ https://stackoverflow.com/a/30058928/6786941 ]

Wasn't a big fan of any of the answers here so came up with a directive of my own. It formats number with white space. Doesn't use jquery and you don't have to track key strokes.

.directive('reformatPhoneNumber', function() {
    return {
      require: 'ngModel',
      link: function(scope, element, attrs, modelCtrl) {

        modelCtrl.$parsers.push(function(number) {
          var transformedNumber = number;

          if (number.match(/^\d{4}$/)) {
            transformedNumber = number.slice(0, 3) + " " + number.slice(3);
          }
          if(number.match(/^[\d\s]{8}$/)){
            transformedNumber = number.slice(0, 7) + " " + number.slice(7);
          }

          if (number.length > 12) {
            transformedNumber = number.slice(0, 12);
          }
          if (transformedNumber !== number) {
            modelCtrl.$setViewValue(transformedNumber);
            modelCtrl.$render();
          }
          return transformedNumber;
        });
      }
    };
  });
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top