Format telephone and credit card numbers in AngularJS

AngularjsNumber Formatting

Angularjs Problem Overview


Question one (formatting telephone number):

I'm having to format a telephone number in AngularJS but there is no filter for it. Is there a way to use filter or currency to format 10 digits to (555) 555-5255? and still preserve the data type of the field as integer?

Question two (masking credit card number):

I have a credit card field that is mapped to AngularJS, like:

<input type="text" ng-model="customer.creditCardNumber"> 

which is returning the whole number (4111111111111111). I will like to mask it with xxx the first 12 digits and only show the last 4. I was thinking on using filter: limit for this but am not clear as how. Any ideas? Is there a way to also format the number with dashes but still retain the data type as integer? sort of 4111-1111-1111-1111.

Angularjs Solutions


Solution 1 - Angularjs

Also, if you need to format telephone number on output only, you can use a custom filter like this one:

angular.module('ng').filter('tel', function () {
    return function (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 10: // +1PPP####### -> C (PPP) ###-####
                country = 1;
                city = value.slice(0, 3);
                number = value.slice(3);
                break;

            case 11: // +CPPP####### -> CCC (PP) ###-####
                country = value[0];
                city = value.slice(1, 4);
                number = value.slice(4);
                break;

            case 12: // +CCCPP####### -> CCC (PP) ###-####
                country = value.slice(0, 3);
                city = value.slice(3, 5);
                number = value.slice(5);
                break;

            default:
                return tel;
        }

        if (country == 1) {
            country = "";
        }

        number = number.slice(0, 3) + '-' + number.slice(3);

        return (country + " (" + city + ") " + number).trim();
    };
});

Then you can use this filter in your template:

{{ phoneNumber | tel }}
<span ng-bind="phoneNumber | tel"></span>

Solution 2 - Angularjs

I created an AngularJS module to handle this issue regarding phonenumbers for myself with a custom directive and accompanying filter.

jsfiddle example: http://jsfiddle.net/aberke/s0xpkgmq/

Filter use example: <p>{{ phonenumberValue | phonenumber }}</p>

Filter code:

.filter('phonenumber', function() {
    /* 
    Format phonenumber as: c (xxx) xxx-xxxx
    	or as close as possible if phonenumber length is not 10
    	if c is not '1' (country code not USA), does not use country code
    */
    
    return function (number) {
	    /* 
	    @param {Number | String} number - Number that will be formatted as telephone number
	    Returns formatted number: (###) ###-####
	    	if number.length < 4: ###
	    	else if number.length < 7: (###) ###

	    Does not handle country codes that are not '1' (USA)
	    */
        if (!number) { return ''; }

        number = String(number);

        // Will return formattedNumber. 
        // If phonenumber isn't longer than an area code, just show number
        var formattedNumber = number;

		// if the first character is '1', strip it out and add it back
		var c = (number[0] == '1') ? '1 ' : '';
		number = number[0] == '1' ? number.slice(1) : number;

		// # (###) ###-#### as c (area) front-end
		var area = number.substring(0,3);
		var front = number.substring(3, 6);
		var end = number.substring(6, 10);

		if (front) {
			formattedNumber = (c + "(" + area + ") " + front);	
		}
		if (end) {
			formattedNumber += ("-" + end);
		}
		return formattedNumber;
    };
});

Directive use example:

<phonenumber-directive placeholder="'Input phonenumber here'" model='myModel.phonenumber'></phonenumber-directive>

Directive code:

.directive('phonenumberDirective', ['$filter', function($filter) {
	/*
	Intended use:
		<phonenumber-directive placeholder='prompt' model='someModel.phonenumber'></phonenumber-directive>
	Where:
		someModel.phonenumber: {String} value which to bind only the numeric characters [0-9] entered
			ie, if user enters 617-2223333, value of 6172223333 will be bound to model
		prompt: {String} text to keep in placeholder when no numeric input entered
	*/

	function link(scope, element, attributes) {

		// scope.inputValue is the value of input element used in template
		scope.inputValue = scope.phonenumberModel;

		scope.$watch('inputValue', function(value, oldValue) {
			
			value = String(value);
			var number = value.replace(/[^0-9]+/g, '');
			scope.phonenumberModel = number;
			scope.inputValue = $filter('phonenumber')(number);
		});
	}
	
	return {
		link: link,
		restrict: 'E',
		scope: {
			phonenumberPlaceholder: '=placeholder',
			phonenumberModel: '=model',
		},
		// templateUrl: '/static/phonenumberModule/template.html',
		template: '<input ng-model="inputValue" type="tel" class="phonenumber" placeholder="{{phonenumberPlaceholder}}" title="Phonenumber (Format: (999) 9999-9999)">',
	};
}])

Full code with module and how to use it: https://gist.github.com/aberke/042eef0f37dba1138f9e

Solution 3 - Angularjs

As shailbenq suggested, phoneformat is awesome.

Include phone format in your website. Create a filter for the angular module or your application.

angular.module('ng')
.filter('tel', function () {
    return function (phoneNumber) {
        if (!phoneNumber)
            return phoneNumber;

        return formatLocal('US', phoneNumber); 
    }
});

Then you can use the filter in your HTML.

{{phone|tel}} 
OR
<span ng-bind="phone|tel"></span>

If you want to use the filter in your controller.

var number = '5553219876';
var newNumber = $filter('tel')(number);

Solution 4 - Angularjs

enter image description here

I also found that JQuery plugin that is easy to include in your Angular App (also with bower :D ) and which check all possible country codes with their respective masks : intl-tel-input

You can then use the validationScript option in order to check the validity of the input's value.

Solution 5 - Angularjs

Angular-ui has a directive for masking input. Maybe this is what you want for masking (unfortunately, the documentation isn't that great):

http://angular-ui.github.com/

I don't think this will help with obfuscating the credit card number, though.

Solution 6 - Angularjs

You also can check input mask formatter.

This is a directive and it's called ui-mask and also it's a part of angular-ui.utils library.

Here is working: Live example

> For the time of writing this post there aren't any examples of using this directive, so I've made a very simple example to demonstrate how this thing works in practice.

Solution 7 - Angularjs

This is the simple way. As basic I took it from http://codepen.io/rpdasilva/pen/DpbFf, and done some changes. For now code is more simply. And you can get: in controller - "4124561232", in view "(412) 456-1232"

Filter:

myApp.filter 'tel', ->
  (tel) ->
    if !tel
      return ''
    value = tel.toString().trim().replace(/^\+/, '')

    city = undefined
    number = undefined
    res = null
    switch value.length
      when 1, 2, 3
        city = value
      else
        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
      res = ('(' + city + ') ' + number).trim()
    else
      res = '(' + city
    return res

And directive:

myApp.directive 'phoneInput', ($filter, $browser) ->

  require: 'ngModel'
  scope:
    phone: '=ngModel'
  link: ($scope, $element, $attrs) ->

    $scope.$watch "phone", (newVal, oldVal) ->
      value = newVal.toString().replace(/[^0-9]/g, '').slice 0, 10
      $scope.phone = value
      $element.val $filter('tel')(value, false)
      return
    return

Solution 8 - Angularjs

Try using phoneformat.js (http://www.phoneformat.com/), you can not only format phone number based on user locales (en-US, ja-JP, fr-FR, de-DE etc) but it also validates the phone number. Its very robust library based on googles libphonenumber project.

Solution 9 - Angularjs

I took aberke's solution and modified it to suit my taste.

  • It produces a single input element
  • It optionally accepts extensions
  • For US numbers it skips the leading country code
  • Standard naming conventions
  • Uses class from using code; doesn't make up a class
  • Allows use of any other attributes allowed on an input element

My Code Pen

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

myApp.controller('exampleController',
  function exampleController($scope) {
    $scope.user = { profile: {HomePhone: '(719) 465-0001 x1234'}};
    $scope.homePhonePrompt = "Home Phone";
  });

myApp
/*
    Intended use:
    <phone-number placeholder='prompt' model='someModel.phonenumber' />
    Where: 
      someModel.phonenumber: {String} value which to bind formatted or unformatted phone number

    prompt: {String} text to keep in placeholder when no numeric input entered
*/
.directive('phoneNumber',
  ['$filter',
  function ($filter) {
    function link(scope, element, attributes) {

      // scope.inputValue is the value of input element used in template
      scope.inputValue = scope.phoneNumberModel;

      scope.$watch('inputValue', function (value, oldValue) {

        value = String(value);
        var number = value.replace(/[^0-9]+/g, '');
        scope.inputValue = $filter('phoneNumber')(number, scope.allowExtension);
        scope.phoneNumberModel = scope.inputValue;
      });
    }

    return {
      link: link,
      restrict: 'E',
      replace: true,
      scope: {
        phoneNumberPlaceholder: '@placeholder',
        phoneNumberModel: '=model',
        allowExtension: '=extension'
      },
      template: '<input ng-model="inputValue" type="tel" placeholder="{{phoneNumberPlaceholder}}" />'
    };
  }
  ]
)
/* 
    Format phonenumber as: (aaa) ppp-nnnnxeeeee
    or as close as possible if phonenumber length is not 10
    does not allow country code or extensions > 5 characters long
*/
.filter('phoneNumber', 
  function() {
    return function(number, allowExtension) {
      /* 
      @param {Number | String} number - Number that will be formatted as telephone number
      Returns formatted number: (###) ###-#### x #####
      if number.length < 4: ###
      else if number.length < 7: (###) ###
      removes country codes
      */
      if (!number) {
        return '';
      }

      number = String(number);
      number = number.replace(/[^0-9]+/g, '');
      
      // Will return formattedNumber. 
      // If phonenumber isn't longer than an area code, just show number
      var formattedNumber = number;

      // if the first character is '1', strip it out 
      var c = (number[0] == '1') ? '1 ' : '';
      number = number[0] == '1' ? number.slice(1) : number;

      // (###) ###-#### as (areaCode) prefix-endxextension
      var areaCode = number.substring(0, 3);
      var prefix = number.substring(3, 6);
      var end = number.substring(6, 10);
      var extension = number.substring(10, 15);

      if (prefix) {
        //formattedNumber = (c + "(" + area + ") " + front);
        formattedNumber = ("(" + areaCode + ") " + prefix);
      }
      if (end) {
        formattedNumber += ("-" + end);
      }
      if (allowExtension && extension) {
        formattedNumber += ("x" + extension);
      }
      return formattedNumber;
    };
  }
);

<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="myApp" ng-controller="exampleController">
  <p>Phone Number Value: {{ user.profile.HomePhone || 'null' }}</p>
  <p>Formatted Phone Number: {{ user.profile.HomePhone | phoneNumber }}</p>
        <phone-number id="homePhone"
                      class="form-control" 
                      placeholder="Home Phone" 
                      model="user.profile.HomePhone"
                      ng-required="!(user.profile.HomePhone.length || user.profile.BusinessPhone.length || user.profile.MobilePhone.length)" />
</div>

Solution 10 - Angularjs

I modified the code to output phone in this format Value: +38 (095) 411-22-23 Here you can check it enter link description here

    var myApp = angular.module('myApp', []);
 
myApp.controller('MyCtrl', function($scope) {
  $scope.currencyVal;
});

myApp.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,12);
            });

            // 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);
            });
        }

    };
});
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, num1, num2, num3;

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

            default:
                country = value.slice(0, 2);
                city = value.slice(2, 5);
                num1 = value.slice(5,8);
                num2 = value.slice(8,10);
                num3 = value.slice(10,12);            
        }

        if(country && city && num1 && num2 && num3){
            return ("+" + country+" (" + city + ") " + num1 +"-" + num2 + "-" + num3).trim();
        }
        else if(country && city && num1 && num2) {
            return ("+" + country+" (" + city + ") " + num1 +"-" + num2).trim();
        }else if(country && city && num1) {
            return ("+" + country+" (" + city + ") " + num1).trim();
        }else if(country && city) {
            return ("+" + country+" (" + city ).trim();
        }else if(country ) {
            return ("+" + country).trim();
        }

    };
});

Solution 11 - Angularjs

You can use ng-pattern which is more easy and more light. http://tutorialzine.com/2014/12/learn-regular-expressions-in-20-minutes/. Here u can know about it,,,just some meaningful words,,,not needs any directive or filter,,,,

Solution 12 - Angularjs

I solved this problem with a custom Angular filter as well, but mine takes advantage of regex capturing groups and so the code is really short. I pair it with a separate stripNonNumeric filter to sanitize the input:

app.filter('stripNonNumeric', function() {
    return function(input) {
        return (input == null) ? null : input.toString().replace(/\D/g, '');
    }
});

The phoneFormat filter properly formats a phone number with or without the area code. (I did not need international number support.)

app.filter('phoneFormat', function() {
    //this establishes 3 capture groups: the first has 3 digits, the second has 3 digits, the third has 4 digits. Strings which are not 7 or 10 digits numeric will fail.
    var phoneFormat = /^(\d{3})?(\d{3})(\d{4})$/;
    
    return function(input) {
        var parsed = phoneFormat.exec(input);
        
        //if input isn't either 7 or 10 characters numeric, just return input
        return (!parsed) ? input : ((parsed[1]) ? '(' + parsed[1] + ') ' : '') + parsed[2] + '-' + parsed[3];
    }
});

Use them simply:

<p>{{customer.phone | stripNonNumeric | phoneFormat}}</p>

The regex for the stripNonNumeric filter came from here.

Solution 13 - Angularjs

You will need to create custom form controls (as directives) for the phone number and the credit card. See section "Implementing custom form control (using ngModel)" on the forms page.

As Narretz already mentioned, Angular-ui's Mask directive should help get you started.

Solution 14 - Angularjs

Simple filter something like this (use numeric class on input end filter charchter in []):

<script type="text/javascript">
// Only allow number input
$('.numeric').keyup(function () {
    this.value = this.value.replace(/[^0-9+-\.\,\;\:\s()]/g, ''); // this is filter for telefon number !!!
});

Solution 15 - Angularjs

Here is the way I created ssn directive which checks for the the pattern and I have used RobinHerbots jquery.inputmask

angular.module('SocialSecurityNumberDirective', [])
       .directive('socialSecurityNumber', socialSecurityNumber);

function socialSecurityNumber() {
	var jquery = require('jquery');
	var inputmask = require("jquery.inputmask");
    return {
        require: 'ngModel',
        restrict: 'A',
        priority: 1000,
        link: function(scope,element, attr, ctrl) {
        	
        	var jquery_element = jquery(element);
        	jquery_element.inputmask({mask:"***-**-****",autoUnmask:true});
            jquery_element.on('keyup paste focus blur', function() {
                var val = element.val();    
                ctrl.$setViewValue(val);
                ctrl.$render();
               
             });

            var pattern = /^\d{9}$/;
			
			var newValue = null;

            ctrl.$validators.ssnDigits = function(value) {
            	 newValue = element.val();
                return newValue === '' ? true : pattern.test(newValue);    
            };
        }
    };
}

Solution 16 - Angularjs

Find Plunker for Formatting Credit Card Numbers using angularjs directive. Format Card Numbers in xxxxxxxxxxxx3456 Fromat.

angular.module('myApp', [])

   .directive('maskInput', function() {
    return {
        	require: "ngModel",
            restrict: "AE",
            scope: {
                ngModel: '=',
             },
            link: function(scope, elem, attrs) {
            	var orig = scope.ngModel;
            	var edited = orig;
                scope.ngModel = edited.slice(4).replace(/\d/g, 'x') + edited.slice(-4);
            	
            	elem.bind("blur", function() {
                	var temp;
                	orig  = elem.val();
                	temp = elem.val();
                    elem.val(temp.slice(4).replace(/\d/g, 'x') + temp.slice(-4));
                });
            	 
            	elem.bind("focus", function() {
                    elem.val(orig);
               });	
            }
       };
   })
  .controller('myCtrl', ['$scope', '$interval', function($scope, $interval) {
    $scope.creditCardNumber = "1234567890123456";
  }]);

Solution 17 - Angularjs

>Inject 'xeditable' module in your angular app(freely available):

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

And then use its built in feature in your HTML code as follows:

<div>{{ value|number:2 }}</div>

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionJ CastilloView Question on Stackoverflow
Solution 1 - AngularjskstepView Answer on Stackoverflow
Solution 2 - AngularjsaberkeView Answer on Stackoverflow
Solution 3 - AngularjsMichael DePetrilloView Answer on Stackoverflow
Solution 4 - AngularjslightalexView Answer on Stackoverflow
Solution 5 - AngularjsNarretzView Answer on Stackoverflow
Solution 6 - AngularjsEgelView Answer on Stackoverflow
Solution 7 - Angularjsyavor.makcView Answer on Stackoverflow
Solution 8 - AngularjsshailbenqView Answer on Stackoverflow
Solution 9 - AngularjstoddmoView Answer on Stackoverflow
Solution 10 - AngularjsВадим ВысоцкийView Answer on Stackoverflow
Solution 11 - AngularjsMohaimin MoinView Answer on Stackoverflow
Solution 12 - AngularjsThe DIMM ReaperView Answer on Stackoverflow
Solution 13 - AngularjsMark RajcokView Answer on Stackoverflow
Solution 14 - AngularjsYej. M.View Answer on Stackoverflow
Solution 15 - AngularjsRaghu ChaitanyaView Answer on Stackoverflow
Solution 16 - AngularjsSagar BhosaleView Answer on Stackoverflow
Solution 17 - AngularjsTechflashView Answer on Stackoverflow