Angular, input field with a currency mask directive for money format on the fly

前端 未结 5 1872
被撕碎了的回忆
被撕碎了的回忆 2020-12-04 18:10

I\'m trying to create an input mask for a EU money field using http://jquerypriceformat.com/

So far in my directive, the input shows correctly to the user with the

相关标签:
5条回答
  • 2020-12-04 18:53

    Here's a way to handle this without jQuery using only an Angular directive. This example doesn't support decimals. It's easy to modify it to support that though, just change the $filter in the toView() function.

    In my opinion this is a better approach to solve the same problem, as you can avoid loading in jQuery and the currency plugin mentioned by the author. Locale support for Euro should be supported by the use the $locale properties, but I've only tested this for use in USD.

    (function() {
      var app = angular.module('currencyMask', []);
    
      // Controller
      app.controller('ctrl', ['$scope', function($scope) {
        $scope.amount = 100000;
      }]);
    
      // Directive
      app.directive('inputCurrency', ['$locale', '$filter', function($locale, $filter) {
    
        // For input validation
        var isValid = function(val) {
          return angular.isNumber(val) && !isNaN(val);
        };
    
        // Helper for creating RegExp's
        var toRegExp = function(val) {
          var escaped = val.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&');
          return new RegExp(escaped, 'g');
        };
    
        // Saved to your $scope/model
        var toModel = function(val) {
    
          // Locale currency support
          var decimal = toRegExp($locale.NUMBER_FORMATS.DECIMAL_SEP);
          var group = toRegExp($locale.NUMBER_FORMATS.GROUP_SEP);
          var currency = toRegExp($locale.NUMBER_FORMATS.CURRENCY_SYM);
    
          // Strip currency related characters from string
          val = val.replace(decimal, '').replace(group, '').replace(currency, '').trim();
    
          return parseInt(val, 10);
        };
    
        // Displayed in the input to users
        var toView = function(val) {
          return $filter('currency')(val, '$', 0);
        };
    
        // Link to DOM
        var link = function($scope, $element, $attrs, $ngModel) {
          $ngModel.$formatters.push(toView);
          $ngModel.$parsers.push(toModel);
          $ngModel.$validators.currency = isValid;
    
          $element.on('keyup', function() {
            $ngModel.$viewValue = toView($ngModel.$modelValue);
            $ngModel.$render();
          });
        };
    
        return {
          restrict: 'A',
          require: 'ngModel',
          link: link
        };
      }]);
    })();
    <script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.6.5/angular.min.js"></script>
    
    <div ng-app="currencyMask" ng-controller="ctrl">
    	<input input-currency ng-model="amount">
    	<p><strong>Amount:</strong> {{ amount }}</p>
    </div>

    0 讨论(0)
  • 2020-12-04 18:55

    I like Dubilla's approach cause of its simplicity and elegance. I decided to add (with due credits) some features on top of it to make it quite close to real world use case.

    I've using it on a github project to create some useful finance directives github.

    Notable extra features:

    1. It does some rigorous checking of the inputs to give a valid response.
    2. It has some keyboard shortcuts to make entering large numbers quicker.
    3. I show how to integrate it with bootstrap and ngmodel css updates.
    4. As a bonus I outputed the form's ngmonel as JSON to help people see how the form validation works in real time

    It also uses a POJO as the ngmodel:

    function Money() {
        this.notional = 0;
        this.maxValue = 99999999999.9;
        this.maxValueString = "99,999,999,999.9";
        this.maxPrecision = 10;
    }
    

    you can use it with Bootstrap 3 like so:

    <h1>Currency Formatting directive</h1>
    
    <div class="row">
    
        <div class="col-md-6">
            <form name="myForm">
    
                <div class="form-group" ng-class="{'has-error': myForm.notional.$invalid && myForm.notional.$touched}">
                    <input type="text" ng-model="myForm.money.notional  " money="money" money-input size="30" required
                           name="notional"
                           class="form-control"
                           placeholder="Enter notional amount"/>
    
                          <p class="help-block error" ng-show="myForm.notional.$error.required && myForm.notional.$touched">Required</p>
    
    
    
                </div>
    
                <button ng-disabled="myForm.$invalid" type="submit">SAVE</button>
            </form>
            <h2>Tips</h2>
            <ol>
    
                <li> Entering 'k' will multiply the amount by one thousand</li>
                <li> Entering 'm' will multiply the amount by one million</li>
                <li> Entering 'b' will multiply the amount by one billion</li>
            </ol>
        </div>
    </div>
    <p>Form debugger</p>
    <pre>
                   form = {{ myForm | json }}
        </pre>
    
    0 讨论(0)
  • 2020-12-04 19:07

    Push a $parser to the controller, and only update the value when it doesn't match the input using $setViewValue() and $render().

    app.directive('currencyInput', function() {
        return {
          require: '?ngModel',
          link: function($scope, element, attrs, controller) {
            return ctrl.$parsers.push(function(inputValue) {
    
                ...
    
                if (result != inputValue) {
                    controller.$setViewValue(res);
                    controller.$render();
                }
            });
          }
        };
    });
    

    Here's a fiddle with the logic I used for my currency input directive: Fiddle

    0 讨论(0)
  • 2020-12-04 19:11

    From your example I don't see that link returns something.

    I would write directive something like:

    .directive('format', ['$filter', function ($filter) {
        return {
            require: '?ngModel',
            link: function (scope, elem, attrs, ctrl) {
                if (!ctrl) return;
    
    
                ctrl.$formatters.unshift(function (a) {
                    return $filter(attrs.format)(ctrl.$modelValue)
                });
    
    
                ctrl.$parsers.unshift(function (viewValue) {
    
              elem.priceFormat({
                prefix: '',
                centsSeparator: ',',
                thousandsSeparator: '.'
            });                
    
                    return elem[0].value;
                });
            }
        };
    }]);
    

    Demo 1 Fiddle

    enter image description here

    If you want on start fire the filter, use $formatters:

    Now link is:

    link: function (scope, elem, attrs, ctrl) {
                if (!ctrl) return;
    
                var format = {
                        prefix: '',
                        centsSeparator: ',',
                        thousandsSeparator: ''
                    };
    
                ctrl.$parsers.unshift(function (value) {
                    elem.priceFormat(format);
    
                    return elem[0].value;
                });
    
                ctrl.$formatters.unshift(function (value) {
                    elem[0].value = ctrl.$modelValue * 100 ;
                    elem.priceFormat(format);
                    return elem[0].value;
                })
            }
    

    Demo 2 Fiddle

    0 讨论(0)
  • 2020-12-04 19:13

    Late to the party, but I believe this deserves another answer! I've been using the ng-currency module. It's absolutely fantastic.

    0 讨论(0)
提交回复
热议问题