【问题标题】:Format telephone and credit card numbers in AngularJS在 AngularJS 中格式化电话号码和信用卡号码
【发布时间】:2012-09-23 21:19:07
【问题描述】:

问题一(格式化电话号码):

我必须在 AngularJS 中格式化电话号码,但没有过滤器。有没有办法使用过滤器或货币将 10 位数字格式化为(555) 555-5255?并且仍然将字段的数据类型保留为整数?

问题二(屏蔽信用卡号):

我有一个映射到 AngularJS 的信用卡字段,例如:

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

返回整数 (4111111111111111)。我想用 xxx 的前 12 位数字掩盖它,只显示最后 4 位。我正在考虑使用 filter:limit ,但不清楚如何。有任何想法吗?有没有办法用破折号格式化数字但仍将数据类型保留为整数?有点像4111-1111-1111-1111

【问题讨论】:

  • 电话号码和信用卡号码都是复杂的结构,可能需要复杂的 UI。他们每个人都应该提出自己的问题,而不是像这样被卷入一个问题。
  • 关于你的问题一:不要在非数字字段中使用整数作为数据类型。电话、邮政编码和文件号码不是数字(你永远不会用它们做任何数学运算),最好将它们视为字符串。

标签: angularjs number-formatting


【解决方案1】:

Angular-ui 有一个用于屏蔽输入的指令。也许这就是你想要的掩蔽(不幸的是,文档不是那么好):

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

不过,我认为这不会有助于混淆信用卡号码。

【讨论】:

  • 试试这个文档:ng-newsletter.com/posts/angular-ui-utils.html。忽略 mask.js 和 'ui.mask' 部分。而是在添加依赖项时使用 ui.utils.js 和 'ui.utils'。
  • 如果文档不是很好,请提供一个工作示例来克服它。谢谢。
【解决方案2】:

您需要为电话号码和信用卡创建自定义表单控件(作为指令)。请参阅forms 页面上的“实现自定义表单控件(使用 ngModel)”部分。

正如 Narretz 已经提到的,Angular-ui 的 Mask directive 应该可以帮助您入门。

【讨论】:

    【解决方案3】:

    另外,如果您只需要在输出时格式化电话号码,您可以使用像这样的自定义过滤器:

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

    然后你可以在你的模板中使用这个过滤器:

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

    【讨论】:

    • 我继续尝试小提琴,但缺少一些我无法弄清楚的东西。这是:jsfiddle.net/jorgecas99/S7aSj
    • 就像一个魅力!是我的错,我有一些错别字。这是更新的小提琴:jsfiddle.net/jorgecas99/S7aSj
    • 功能很棒。您可以在输入 ng 绑定中使用过滤器吗?没有将其应用于输入。为此,我使用了 ui-mask,但它不太优雅:/
    • J Castill,在你小提琴的第 36 行,我会这样做: country = 1 ? "" : 国家;
    • 国际号码还是有点破。
    【解决方案4】:

    尝试使用 phoneformat.js (http://www.phoneformat.com/),您不仅可以根据用户区域设置(en-US、ja-JP、fr-FR、de-DE 等)格式化电话号码,还可以验证电话号码.它基于 google 的 libphonenumber 项目非常强大的库。

    【讨论】:

      【解决方案5】:

      正如 shailbenq 所建议的,phoneformat 很棒。

      在您的网站中包含电话格式。为角度模块或您的应用程序创建一个过滤器。

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

      然后你就可以在你的 HTML 中使用过滤器了。

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

      如果您想在控制器中使用过滤器。

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

      【讨论】:

      • 除非 i18n,否则我更喜欢 AngularUI 的 ui.mask
      【解决方案6】:

      我还发现 JQuery 插件很容易包含在您的 Angular 应用程序中(也可以使用 bower :D ),并且可以使用各自的掩码检查所有可能的国家/地区代码: intl-tel-input

      然后您可以使用validationScript 选项来检查输入值的有效性。

      【讨论】:

      • 他们有什么办法可以删除 jquery 插件吗?
      【解决方案7】:

      我创建了一个 AngularJS 模块,通过自定义指令和随附的过滤器为自己处理有关电话号码的问题。

      jsfiddle 示例:http://jsfiddle.net/aberke/s0xpkgmq/

      过滤器使用示例: &lt;p&gt;{{ phonenumberValue | phonenumber }}&lt;/p&gt;

      过滤代码:

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

      指令使用示例:

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

      指令代码:

      .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)">',
          };
      }])
      

      带有模块的完整代码以及如何使用它: https://gist.github.com/aberke/042eef0f37dba1138f9e

      【讨论】:

      • 当我保存我的表单并再次打开它时,数字没有被保存。你知道它正在发生吗?
      【解决方案8】:

      您也可以查看输入掩码格式化程序

      这是一个指令,它被称为 ui-mask,它也是 angular-ui.utils 库的一部分。

      这里正在运行:Live example

      在写这篇文章的时候,还没有任何使用这个指令的例子,所以我做了一个非常简单的例子来演示这个东西在实践中是如何工作的。

      【讨论】:

      【解决方案9】:

      您可以使用更简单、更轻便的 ng-pattern。 http://tutorialzine.com/2014/12/learn-regular-expressions-in-20-minutes/。 在这里你可以知道,,,只是一些有意义的词,,,不需要任何指令或过滤器,,,,

      【讨论】:

        【解决方案10】:

        这是简单的方法。作为基本,我从http://codepen.io/rpdasilva/pen/DpbFf 获取它,并做了一些更改。现在代码更简单。 你可以得到:在控制器中——“4124561232”,在视图中“(412) 456-1232

        过滤器:

        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
        

        和指令:

        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
        

        【讨论】:

          【解决方案11】:

          我采用了 aberke 的解决方案并对其进行了修改以适合我的口味。

          • 它产生单个输入元素
          • 它可以选择接受扩展
          • 对于美国号码,它会跳过主要的国家/地区代码
          • 标准命名约定
          • 使用代码中的类;不组成一个班级
          • 允许使用输入元素上允许的任何其他属性

          我的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>

          【讨论】:

            【解决方案12】:

            类似这样的简单过滤器(在[]中的输入端过滤器字符上使用数字类):

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

            【讨论】:

              【解决方案13】:

              我修改了代码以这种格式输出电话值:+38 (095) 411-22-23 这里可以查看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();
                      }
              
                  };
              });
              

              【讨论】:

                【解决方案14】:

                这是我创建检查模式的 ssn 指令的方式,我使用了 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);    
                            };
                        }
                    };
                }
                

                【讨论】:

                  【解决方案15】:

                  我也使用自定义 Angular 过滤器解决了这个问题,但我的利用了正则表达式捕获组,因此代码非常短。我将它与单独的 stripNonNumeric 过滤器配对以净化输入:

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

                  phoneFormat 过滤器可以正确格式化带有或不带有区号的电话号码。 (我不需要国际号码支持。)

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

                  简单地使用它们:

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

                  stripNonNumeric 过滤器的正则表达式来自 here

                  【讨论】:

                    【解决方案16】:

                    使用 angularjs 指令查找 Plunker for Formatting Credit Card Numbers。将卡号格式化为 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";
                      }]);
                    

                    【讨论】:

                      【解决方案17】:

                      在您的 Angular 应用程序中注入“xeditable”模块(免费提供):

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

                      然后在您的 HTML 代码中使用其内置功能,如下所示:

                      &lt;div&gt;{{ value|number:2 }}&lt;/div&gt;

                      【讨论】:

                        猜你喜欢
                        • 2015-05-16
                        • 2016-03-07
                        • 1970-01-01
                        • 2015-11-19
                        • 2020-02-05
                        • 2021-10-20
                        • 1970-01-01
                        相关资源
                        最近更新 更多