1

I have this $parser which limits the number of characters that user has entered:

  var maxLength = attrs['limit'] ? parseInt(attrs['limit']) : 11;

  function fromUser(inputText) {
      if (inputText) {
          if (inputText.length > maxLength) {
              var limitedText = inputText.substr(0, maxLength);
              ngModel.$setViewValue(limitedText);
              ngModel.$render();
              return limitedText;
          }
      }
      return inputText;
  }

  ngModel.$parsers.push(fromUser);

I want to use this directive on an input element which has ng-model-options="{updateOn: 'blur'}", but there is a problem that the whole $parser thing gets executed after user loses the focus of input element, I want it to get executed as user types into the input field.

(function (angular) {
    "use strict";
    angular.module('app', [])
    .controller("MainController", function($scope) {
      $scope.name = "Boom !";
      $scope.name2 = "asdf";
    }).directive('limitCharacters', limitCharactersDirective);

    function limitCharactersDirective() {
        return {
            restrict: "A",
            require: 'ngModel',
            link: linkFn
        };

        function linkFn(scope, elem, attrs, ngModel) {
            var maxLength = attrs['limit'] ? parseInt(attrs['limit']) : 11;

            function fromUser(inputText) {
                if(inputText) {
                    if (inputText.length > maxLength) {
                        var limitedText = inputText.substr(0, maxLength);
                        ngModel.$setViewValue(limitedText);
                        ngModel.$render();
                        return limitedText;
                    }
                }
                return inputText;
            }

            ngModel.$parsers.push(fromUser);
        }
    }

})(angular);
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.6.1/angular.js"></script>

<div ng-app="app">
  without ng-model-options: <input type="text" ng-model="name" limit-characters limit="7" />
  
  <br>
  with ng-model-options <input type="text" ng-model="name2" ng-model-options="{updateOn: 'blur'}" limit-characters limit="7" />
</div>
Rathma
  • 1,196
  • 2
  • 33
  • 65

1 Answers1

1

When a user inputs some value in input $setViewValue is called internally. As stated in angular docs this is what $setViewValue does:

When $setViewValue is called, the new value will be staged for committing through the $parsers and $validators pipelines. If there are no special ngModelOptions specified then the staged value is sent directly for processing through the $parsers pipeline. After this, the $validators and $asyncValidators are called and the value is applied to $modelValue. Finally, the value is set to the expression specified in the ng-model attribute and all the registered change listeners, in the $viewChangeListeners list are called.

In case the ngModelOptions directive is used with updateOn and the default trigger is not listed, all those actions will remain pending until one of the updateOn events is triggered on the DOM element.

What it means is that $parsers are used only on actual model change commit (which in your case happens on blur).

The easiest solution to your problem would be to not use $parsers to limit the characters. Check other solutions in this question.

Using the most upvoted answer you can modify your directive to make it look like this:

function limitCharactersDirective() {
    return {
        restrict: "A",
        require: 'ngModel',
        link: linkFn
    };

    function linkFn(scope, elem, attrs, ngModel) {
        var maxLength = attrs['limit'] ? parseInt(attrs['limit']) : 11;
        angular.element(elem).on("keypress", function(e) {
            if (this.value.length == maxLength) e.preventDefault();
        });
    }
}

Check this JSFiddle for a working example.

Community
  • 1
  • 1
mczerwi
  • 490
  • 2
  • 11
  • very well written, but I have one issue with the new directive , when user copy and paste into the directive, it wont notices it. any idea? – Rathma May 14 '17 at 07:22