How to make a percent formatted input work on latest AngularJS?

后端 未结 4 2104
心在旅途
心在旅途 2020-12-14 20:56

I saw this solution http://jsfiddle.net/gronky/GnTDJ/ and it works. That is, when you input 25, it is pushed back to model as 0.25

HTML:



        
相关标签:
4条回答
  • 2020-12-14 21:36

    The fiddle doesn't work with current Angular version since quite a few APIs have changed since. angular.formatter is no longer available and neither is angular.filter.

    The way to write it now is to use a directive and make use of $parser and $formatter available on the directive controller. So your link function will look something like

    link: function(scope, ele, attr, ctrl){
            ctrl.$parsers.unshift(
                function(viewValue){
                    return $filter('number')(parseFloat(viewValue)/100, 2);
                }
            );
            ctrl.$formatters.unshift(
                function(modelValue){
                    return $filter('number')(parseFloat(modelValue)*100, 2);
                }
            );
          }
    

    Also the filters are now accessed through $filter service. You can find the documentation here: https://docs.angularjs.org/api/ng/filter/number

    Updated fiddle for the original example: http://jsfiddle.net/abhaga/DdeCZ/18/

    Currency filter is already available in angular: https://docs.angularjs.org/api/ng/filter/currency

    0 讨论(0)
  • 2020-12-14 21:38

    Here's a full directive that will parse, format, and perform Angular validation on the inputs. (Tested against angular 1.2 & 1.3.)

    We use this so that our data model to/from server can be expressed in decimal notation (0.7634), but we provide a human-readable format to the user (76.34), and enforce a maximum precision. Note that this directive is concerned purely with the numeric aspects. I find it easier to insert a '%' into the template separately, rather than including it here.

    It defaults to enforcing input values from -100 to 100, but you can supply your own bounds with attrs pct-min and pct-max.

    'use strict';
    
    angular.module('XLDirectives')
      .directive('xlPercentage', function($filter) {
        // A directive for both formatting and properly validating a percentage value. 
        // Assumes that our internal model is expressed as floats -1 to +1: .099 is 9.9%
        // Formats display into percents 1-100, and parses user inputs down to the model. 
        // Parses user input as floats between 0 and 100 into floats less than 1. 
        // Validates user input to be within the range -100 to +100. 
        // Sets Angular $valid property accordingly on the ngModelController.
        // If a `pct-max` or `pct-min` attribute is specified on the <input>, will use those bounds instead.
        // If a `pct-decimals` attr present, will truncate inputs accordingly. 
    
        function outputFormatter(modelValue, decimals) {
          var length = decimals || 2;
          if (modelValue != null) {
            return $filter('number')(parseFloat(modelValue) * 100, length);
          } else {
            return undefined;
          }
        };
    
        function inputParser(viewValue, decimals) {
          var length = decimals || 4;
          if (viewValue != null) {
            return $filter('number')(parseFloat(viewValue) / 100, length);
          } else {
            return undefined;
          }
    
        }
    
        function isWithinBounds(value, upper, lower) {
          if (value >= lower && value <= upper) {
            return true;
          } else {
            return false;
          }
        }
    
        return {
          restrict: 'A',
          require: 'ngModel',
          link: function postLink(scope, element, attrs, ctrl) {
            ctrl.$parsers.unshift(function(viewValue) {
              // confirm the input from the view contains numbers, before parsing
              var numericStatus = viewValue.match(/(\d+)/),
                min = parseFloat(attrs.pctMin) || -100,
                max = parseFloat(attrs.pctMax) || 100,
                decimals = parseFloat(attrs.pctDecimals) || 4,
                bounded = isWithinBounds(viewValue, max, min);
              if (numericStatus !== null && bounded) {
                ctrl.$setValidity('percentage', true);
                // round to max four digits after decimal
                return inputParser(viewValue, decimals);
              } else {
                ctrl.$setValidity('percentage', false);
                return undefined
              }
            });
    
            ctrl.$formatters.unshift(outputFormatter);
            // we have to watch for changes, and run the formatter again afterwards
            element.on('change', function(e) {
              var element = e.target;
              element.value = outputFormatter(ctrl.$modelValue, 2);
            });
          }
        };
      });
    
    
    // REFS: 
    // http://stackoverflow.com/questions/17344828/angularjs-should-i-use-a-filter-to-convert-integer-values-into-percentages
    // http://stackoverflow.com/questions/13668440/how-to-make-a-percent-formatted-input-work-on-latest-angularjs
    
    0 讨论(0)
  • 2020-12-14 21:43

    I modified abhaga's answer to allow for .## and ## input. In my opinion this is a lot more user-friendly

    link: function(scope, element, attr, ngModel) {
                ngModel.$parsers.unshift(
                    function(viewValue){
                        var perc = parseFloat(viewValue);
                        if (perc<0 || perc>100 || !isFinite(perc)){
                            return null;
                        }
                        if (perc>1 && perc<=100){
                            return parseFloat($filter('number')(perc/100));
                        }
                        return perc;
                    }
                );
                ngModel.$formatters.unshift(
                    function(modelValue){
                        if(!isFinite(modelValue)){
                            return "";
                        }
                        return $filter('number')(parseFloat(modelValue)*100, 2);
                    }
                );
            }
    
    0 讨论(0)
  • 2020-12-14 21:49

    Another way to implement percentage filter (works with angular#~1.2):

    angular.module('moduleName')
    .filter('percentage', ['$filter', function($filter) {
        return function(input, decimals) {
            return $filter('number')(input*100, decimals)+'%';
        };
    }]);
    

    How to use it:

    <span>{{someNumber | percentage:2}}</span>
    
    0 讨论(0)
提交回复
热议问题