1

I'm trying to create a directive for an input element. When a number is entered I want to update the input value on the blur event by adding zeros like '12' -> '0012'. I have a parameter passed to directive to specify the length of the result number for example if the length of the number is 6 and the user enters 123 the result is 000123

I have created a directive, but after the blur event, the render view works but it does not change the ng-model value

app.directive('addZeros', function() {
  return {
    restrict: 'A',
    scope: {
      addZeros: '=addZeros'
    },
    require: '?ngModel',

    link: function(scope, element, attrs, modelCtrl) {
      var addZeroOK = false;
      modelCtrl.$parsers.push(function(inputValue) {
        var num = parseInt(inputValue, 10);
        num = '' + num;
        var clean = num.replace(/[^0-9]/g, '');
        if (inputValue.length <= scope.addZeros) {
          scope.addZeros = parseInt(scope.addZeros, 10);
          if (isNaN(clean) || isNaN(scope.addZeros)) {
            return inputValue;
          }

          element.bind('blur', function(event) {
            if (clean.length > 0) {
              addZeroOK = true;
              while (clean.length < scope.addZeros) {
                clean = '0' + clean;
              }
            }
            scope.$apply(function() {
              modelCtrl.$setViewValue(clean);
              modelCtrl.$render();
              return clean;

            })

          });
          if (addZeroOK == false) {
            modelCtrl.$setViewValue(clean);
            modelCtrl.$render();
            return clean;
          }
        } else {
          var str = inputValue.slice(0, -1);
          modelCtrl.$setViewValue(str);
          modelCtrl.$render();
          return str;
        }
      });
    }
  };
});

Plnker: http://plnkr.co/edit/1ON1DtKZ6qjmT1i2U5pz?p=preview

Steffi Keran Rani J
  • 3,667
  • 4
  • 34
  • 56
Med
  • 117
  • 1
  • 14
  • Please check this link, looks like the problem is when you have input type="number", try to change it to text with pattern.Hope this will show you the way) [link](https://stackoverflow.com/questions/5345095/chrome-auto-formats-input-number) – Beny Feb 08 '18 at 09:18
  • Looks like this guy had same issue please check, his solution [link](http://theangularjs.com/leading-zeros-missing-within-number-input.html) – Beny Feb 08 '18 at 09:23
  • thank you @Beny , but my problem is getting the value in my **ng-model** the add of zero works and in the input i have the rigth value but the ng-modal is empty you can check the link http://plnkr.co/edit/1ON1DtKZ6qjmT1i2U5pz?p=preview for mor detials – Med Feb 08 '18 at 10:02
  • correct, i missed that. – Beny Feb 08 '18 at 11:02

2 Answers2

1

The directive code runs past your if statement and returns "undefined"(as there is no return statement). See Plunker.

app.directive('addZeros', function() {
  return {
    restrict: 'A',
    scope: {
      addZeros: '=addZeros'
    },
    require: '?ngModel',

    link: function(scope, element, attrs, modelCtrl) {
      var addZeroOK = false;
      modelCtrl.$parsers.push(function(inputValue) {
        var num = parseInt(inputValue, 10);
        num = '' + num;
        var clean = num.replace(/[^0-9]/g, '');
        if (inputValue.length <= scope.addZeros) {
          scope.addZeros = parseInt(scope.addZeros, 10);
          if (isNaN(clean) || isNaN(scope.addZeros)) {
            return inputValue;
          }

          element.bind('blur', function(event) {
            if (clean.length > 0) {
              addZeroOK = true;
              while (clean.length < scope.addZeros) {
                clean = '0' + clean;
              }
            }
            scope.$apply(function() {
              modelCtrl.$setViewValue(clean);
              modelCtrl.$render();
              return clean;

            })

          });
          if (addZeroOK == false) {
            modelCtrl.$setViewValue(clean);
            modelCtrl.$render();
            return clean;
          }
        } else {
          var str = inputValue.slice(0, -1);
          modelCtrl.$setViewValue(str);
          modelCtrl.$render();
          return str;
        }

        return inputValue;

      });
    }
  };
});
Codendaal
  • 514
  • 6
  • 14
1

If you need just to change both view and model values, you don't need $parsers then (and of course this is not a good practice to bind to events inside of the parser since it is firing every-time the $viewValue from the DOM is changed):

angular.module('plunker', [])
    .controller('MainCtrl', function ($scope) {

    })
    .directive('addZeros', function () {
        return {
            restrict: 'A',
            scope: {
                addZeros: '=addZeros'
            },
            require: '?ngModel',

            link: function (scope, element, attrs, modelCtrl) {
                scope.addZeros = parseInt(scope.addZeros, 10);
                var dRegex = new RegExp(/[^0-9.]/g);

                element.on('blur', function (event) {
                    modelCtrl.$setViewValue(getValue(modelCtrl.$modelValue));
                    modelCtrl.$render();
                });

                element.on("keypress", function (event) {
                    // allow only digits to be entered, or backspace and delete keys to be pressed
                    var allow = (event.charCode >= 48 && event.charCode <= 57) ||
                        (event.keyCode === 8 || event.keyCode === 46);

                    if (!allow) {
                        event.preventDefault();
                    }

                });

                function getValue(inputValue) {
                    var num = String(parseInt(inputValue, 10));
                    var clean = num.replace(dRegex, '');

                    if (inputValue.length <= scope.addZeros) {
                        if (!clean || isNaN(scope.addZeros)) {
                            return inputValue;
                        }
                    }

                    if (clean.length > 0) {
                        while (clean.length < scope.addZeros) {
                            clean = '0' + clean;
                        }
                    }
                    return clean;
                }

            }
        };
    });
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.6.7/angular.min.js"></script>
<body ng-app="plunker">

    <div ng-controller="MainCtrl">
        <p>Input below will accept only numbers and add leading zeros</p>
        <h4>value of test1 : {{test1}}</h4>
        <p><input type="tel" ng-model="test1" add-zeros="4" class="form-control"/></p>
        <h4>value of test2 : {{test2}}</h4>
        <p><input type="tel" ng-model="test2" add-zeros="6" class="form-control"/></p>
    </div>

</body>

UPDATE: Modified to restrict typing non digits.

Stanislav Kvitash
  • 4,614
  • 18
  • 29
  • thank you @Stanislav Kvitash for you advise but i want with this directive to accept just number – Med Feb 08 '18 at 10:29
  • @AmineAlaoui and it accepts numbers, isn't it? Or do you mean you want to restrict typing non digits into it? Accepted answer either doesn't prevent of typing anything into input and as I said - it is not good to use `element.bind` inside of the $parser function. Just add `console.log` and see how many times is been called over the time while you are typing. – Stanislav Kvitash Feb 08 '18 at 10:47
  • yes exactly i want also with adding zeros restrict typing non digits character – Med Feb 08 '18 at 11:10