frog
frog

Reputation: 93

angular.js capitalize each word

This function should capitalize first letter for each word in input. But angular is throwing me "RangeError: Maximum call stack size exceeded". And only becouse of that ' ' space on line 9.

myApp.directive('capitalizeFirst', function(uppercaseFilter, $parse) {
   return {
     require: 'ngModel',
     link: function(scope, element, attrs, modelCtrl) {
        var capitalize = function(inputValue) {
           var capitalized = inputValue.split(' ').reduce(function(prevValue, word){
            return  prevValue + word.substring(0, 1).toUpperCase() + word.substring(1)+' ';
        }, '');
           if(capitalized !== inputValue) {
              modelCtrl.$setViewValue(capitalized);
              modelCtrl.$render();
            }         
            return capitalized;
         }
         var model = $parse(attrs.ngModel);
         modelCtrl.$parsers.push(capitalize);
         capitalize(model(scope));
     }
   };
});

here is the fiddle

http://jsfiddle.net/YyYnM/205/

Can someone explain this to me? I'm trying to figure this out for an hour now.

Upvotes: 2

Views: 2039

Answers (3)

David Michael Gang
David Michael Gang

Reputation: 7299

The problem is here

var capitalize = function(inputValue) {
           var capitalized = inputValue.split(' ').reduce(function(prevValue, word){
            return  prevValue + word.substring(0, 1).toUpperCase() + word.substring(1)+' ';
        }, '');

It adds you an extra space every time you run the function.

Angular works the way that it runs cycles.

Eeverytime a change of one of the variables is detected it runs another cycle.

Here because of the extra space, the string is changed in an infinite loop and therefore angular dies.

An ugly solution is to add afterwards

capitalized = capitalized.substring(0, capitalized.length - 1);

Upvotes: 2

Nikolas
Nikolas

Reputation: 1176

The problem is not the '' on line 9 as you suggested, but your use of the modelCtrl. When you use modelCtrl.$setViewValue modelCtrl will run all the $parsers again. Therefore it will invoke your capitalize function recursivly until it rans out of stack.

All you need to do, to capitalize the input, is to push a function that changes the string to the $parsers array. This will do the job:

myApp.directive('capitalizeFirst', function(uppercaseFilter, $parse) {
   return {
     require: 'ngModel',
     link: function(scope, element, attrs, modelCtrl) {
        var capitalize = function(inputValue) {
           var capitalized = inputValue.split(' ').reduce(function(prevValue, word){
            return  prevValue + word.substring(0, 1).toUpperCase() + word.substring(1)+' ';
        }, '');        
            return capitalized;
         }
         modelCtrl.$parsers.push(capitalize);
     }
   };
});

function MyCtrl($scope) {
    $scope.name = '';
}

Upvotes: 1

Darshan P
Darshan P

Reputation: 2241

I don't know what's wrong with your code but try this. DEMO

myApp.directive('capitalizeFirst', function (uppercaseFilter, $parse) {
    return {
        require: 'ngModel',
        scope: {
            ngModel: "="
        },
        link: function (scope, element, attrs, modelCtrl) {

            scope.$watch("ngModel", function () {
                scope.ngModel = scope.ngModel.replace(/^(.)|\s(.)/g, function(v){ return v.toUpperCase( ); });
            });
        }
    };
});

Upvotes: 2

Related Questions