How to autocapitalize the first character in an input field in AngularJS?
Asked Answered
G

15

56

How to autocapitalize the first character in an input field inside an AngularJS form element?

I saw the jQuery solution already, but believe this has to be done differently in AngularJS by using a directive.

Gilleod answered 6/3, 2013 at 8:44 Comment(0)
M
100

Yes, you need to define a directive and define your own parser function:

myApp.directive('capitalizeFirst', function($parse) {
   return {
     require: 'ngModel',
     link: function(scope, element, attrs, modelCtrl) {
        var capitalize = function(inputValue) {
           if (inputValue === undefined) { inputValue = ''; }
           var capitalized = inputValue.charAt(0).toUpperCase() +
                             inputValue.substring(1);
           if(capitalized !== inputValue) {
              modelCtrl.$setViewValue(capitalized);
              modelCtrl.$render();
            }         
            return capitalized;
         }
         modelCtrl.$parsers.push(capitalize);
         capitalize($parse(attrs.ngModel)(scope)); // capitalize initial value
     }
   };
});

HTML:

<input type="text" ng-model="obj.name" capitalize-first>

Fiddle

Memphis answered 6/3, 2013 at 17:20 Comment(9)
could you explain a little more how this works or provide links to the docs? Also if you type a lowercase letter at the start and the box is not empty the cursor moves to the end, but if you type an uppercase character it doesn't...Flense
@JasonGoemaat, docs.angularjs.org/guide/forms#customvalidation I don't have any ideas on how to fix the cursor movement.Memphis
@JasonGoemaat The cursor movement occurs because the content is being replaced by the changed text (only changed when first letter is entered as non-capital). To fix this you can capture the selection range and reset the new selection range within the new text once it is set. There is a sweet library called Rangy (code.google.com/p/rangy) that will help you with this if you are serious enough about it to fix it.Reinhart
scope[attrs.ngModel] wont work for: <input type="text" ng-model="myObj.user.name" capitalize-first> as attrs.ngModel evaluates to "myObj.user.name" so scope[attrs.ngModel] evaluates to undefined.Gasp
@Andi, good catch. I updated the answer and fiddle to use $parse, which will handle the original case and your scenario.Memphis
Can someone explain why this is better (or worse) than setting a $watch in the controller? Assuming one only needs to use it one time.Thunder
That does not play well with $asyncValidators or ng-model-options="{ debounce: 1000 }".Wakeful
when using $setViewValue, you trigger the parsers and validators again. If you add a console.log statement at the beginning of your capitalize function, you'll see it printed twiceMyeshamyhre
I changed the capitalized variable so it would lowercase the rest of the string: var capitalized = inputValue.charAt(0).toUpperCase() + inputValue.substring(1).toLowerCase();Pumpernickel
F
54

Please remember that not everything needs an Angular solution. You see this a lot with the jQuery crowd; they like to use expensive jQuery functions to do things that are simpler or easier to do with pure javascript.

So while you might very well need a capitalize function and the above answers provide that, it's going to be a lot more efficient to just use the css rule "text-transform: capitalize"

<tr ng-repeat="(key, value) in item">
    <td style="text-transform: capitalize">{{key}}</td>
    <td>{{item}}</td>
</tr>
Filing answered 4/6, 2013 at 15:46 Comment(4)
The OP only wants to capitalize the first letter in the input. This solution capitalizes each wordMerkel
good thoughts, but this wouldn't necessarily work for input fields, if it is submitted (enter input, click save, for instance), I just got bitten by that.Hamrnand
In my case, I landed on this question looking for a "force lowercase" solution. Thanks for posting this even though it wasn't a perfect solution to the OP.Schweitzer
Just what i needed!! Thanks!Coonskin
S
23

You can create a custom filter 'capitalize' and apply it to any string you want:

 <div ng-controller="MyCtrl">
     {{aString | capitalize}} !
</div>

JavaScript code for filter:

var app = angular.module('myApp',[]);

myApp.filter('capitalize', function() {
    return function(input, scope) {
        return input.substring(0,1).toUpperCase()+input.substring(1);
    }
});
Spermatophyte answered 6/3, 2013 at 9:3 Comment(3)
I don't think filters can be applied on input field.Infield
Ain't that such a shame, @tamakisquare; I came here googling for exactly that.Aindrea
How do you do this for multiple words in a sentence?Evanston
B
4

Use the CSS :first-letter pseudo class.

You need to put everything lowercase and after apply the uppercase only to the first letter

p{
    text-transform: lowercase;
}
p:first-letter{
    text-transform: uppercase;
}

Here's an example: http://jsfiddle.net/AlexCode/xu24h/

Beneficial answered 21/3, 2014 at 14:9 Comment(1)
No, I'm afraid not. This applies to content, not to specific properties of an element. On Inputs I'm afraid you have to use javascript.Beneficial
H
4

Modified his code to capitalize every first character of word. If you give 'john doe', output is 'John Doe'

myApp.directive('capitalizeFirst', function() {
   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;
         }
         modelCtrl.$parsers.push(capitalize);
         capitalize(scope[attrs.ngModel]);  // capitalize initial value
     }
   };
});
Hydrometer answered 5/6, 2014 at 2:16 Comment(0)
D
4

I would prefer a filter and directive. This should work with cursor movement:

app.filter('capitalizeFirst', function () {
    return function (input, scope) {
        var text = input.substring(0, 1).toUpperCase() + input.substring(1).toLowerCase();
        return text;
    }
});

app.directive('capitalizeFirst', ['$filter', function ($filter) {
    return {
        require: 'ngModel',
        link: function (scope, element, attrs, controller) {
            controller.$parsers.push(function (value) {
                var transformedInput = $filter('capitalizeFirst')(value);
                if (transformedInput !== value) {
                    var el = element[0];
                    el.setSelectionRange(el.selectionStart, el.selectionEnd);
                    controller.$setViewValue(transformedInput);
                    controller.$render();
                }
                return transformedInput;
            });
        }
    };
}]);

Here is a fiddle

Diegodiehard answered 4/12, 2014 at 9:56 Comment(0)
A
3

To fix the cursor problem (from where Mark Rajcok's solution), you can store element[0].selectionStart at the beginning of your method, and then ensure to reset element[0].selectionStart and element[0].selectionEnd to the stored value before the return. This should capture your selection range in angular

Apterous answered 18/12, 2013 at 20:59 Comment(1)
Can you please give some more detailHasseman
F
3

Generate directive:

ng g directive capitalizeFirst

Update file capitalize-first.directive.ts:

import {Directive, ElementRef, HostListener} from '@angular/core';

@Directive({
  selector: '[appCapitalizeFirst]'
})
export class CapitalizeFirstDirective {

  constructor(private ref: ElementRef) {
  }

  @HostListener('input', ['$event'])
  onInput(event: any): void {
    if (event.target.value.length === 1) {
      const inputValue = event.target.value;
      this.ref.nativeElement.value = inputValue.charAt(0).toUpperCase() + inputValue.substring(1);
    }
  }

}

Usage:

  <input appCapitalizeFirst>

This code woks with Angular 11+

Fredericksburg answered 7/12, 2020 at 9:15 Comment(0)
M
2

Comment to Mark Rajcok solution: when using $setViewValue, you trigger the parsers and validators again. If you add a console.log statement at the beginning of your capitalize function, you'll see it printed twice.

I propose the following directive solution (where ngModel is optional):

.directive('capitalize', function() {
   return {
     restrict: 'A',
     require: '?ngModel',
     link: function(scope, element, attrs, ngModel) {
         var capitalize = function (inputValue) {
             return (inputValue || '').toUpperCase();
         }
         if(ngModel) {
             ngModel.$formatters.push(capitalize);
             ngModel._$setViewValue = ngModel.$setViewValue;
             ngModel.$setViewValue = function(val){
                 ngModel._$setViewValue(capitalize(val));
                 ngModel.$render();
             };
         }else {
             element.val(capitalize(element.val()));
             element.on("keypress keyup", function(){
                 scope.$evalAsync(function(){
                     element.val(capitalize(element.val()));
                 });
             });
         }
     }
   };
});
Myeshamyhre answered 22/4, 2015 at 11:41 Comment(0)
D
1

Here's a codepen for a filter that capitalizes the first letter: http://codepen.io/WinterJoey/pen/sfFaK

angular.module('CustomFilter', []).
  filter('capitalize', function() {
    return function(input, all) {
      return (!!input) ? input.replace(/([^\W_]+[^\s-]*) */g, function(txt){return txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase();}) : '';
    }
  });
Deepsea answered 6/9, 2014 at 19:44 Comment(0)
R
1

Further to the CSS-only answers, you could always use Twitter Bootstrap:

<td class="text-capitalize">
Rickety answered 26/6, 2015 at 5:37 Comment(0)
N
0

Building off Mark Rajcok's solution; It's important to consider that the directive evaluate only when the input field in engaged, otherwise you'll get error messages firing off until the input field has a 1st character. Easy fix with a few conditionals: A jsfiddle to go with that: https://jsfiddle.net/Ely_Liberov/Lze14z4g/2/

      .directive('capitalizeFirst', function(uppercaseFilter, $parse) {
      return {
        require: 'ngModel',
        link: function(scope, element, attrs, modelCtrl) {
            var capitalize = function(inputValue) {
              if (inputValue != null) {
              var capitalized = inputValue.charAt(0).toUpperCase() +
                inputValue.substring(1);
              if (capitalized !== inputValue) {
                 modelCtrl.$setViewValue(capitalized);
                 modelCtrl.$render();
              }
              return capitalized;
            }
          };
          var model = $parse(attrs.ngModel);
          modelCtrl.$parsers.push(capitalize);
          capitalize(model(scope));
        }
       };
    });
Narda answered 16/5, 2015 at 20:57 Comment(0)
C
0

The problem with css-ony answers is that the angular model is not updated with the view. This is because css only applies styling after rendering.

The following directive updates the model AND remembers the cursors location

app.module.directive('myCapitalize', [ function () {
        'use strict';

    return {
        require: 'ngModel',
        restrict: "A",
        link: function (scope, elem, attrs, modelCtrl) {

            /* Watch the model value using a function */
            scope.$watch(function () {
                return modelCtrl.$modelValue;
            }, function (value) {

                /**
                 * Skip capitalize when:
                 * - the value is not defined.
                 * - the value is already capitalized.
                 */
                if (!isDefined(value) || isUpperCase(value)) {
                    return;
                }

                /* Save selection position */
                var start = elem[0].selectionStart;
                var end = elem[0].selectionEnd;

                /* uppercase the value */
                value = value.toUpperCase();

                /* set the new value in the modelControl */
                modelCtrl.$setViewValue(value);

                /* update the view */
                modelCtrl.$render();

                /* Reset the position of the cursor */
                elem[0].setSelectionRange(start, end);
            });

            /**
             * Check if the string is defined, not null (in case of java object usage) and has a length.
             * @param str {string} The string to check
             * @return {boolean} <code>true</code> when the string is defined
             */
            function isDefined(str) {
                return angular.isDefined(str) && str !== null && str.length > 0;
            }

            /**
             * Check if a string is upper case
             * @param str {string} The string to check
             * @return {boolean} <code>true</code> when the string is upper case
             */
            function isUpperCase(str) {
                return str === str.toUpperCase();
            }
        }
    };
}]);
Coax answered 18/8, 2016 at 8:52 Comment(1)
This makes entire text capital. Am looking for only first letter to be capital. And cursor to be remained in its positionHasseman
A
-1

You can use the provided uppercase filter.

http://docs.angularjs.org/api/ng.filter:uppercase

Asbestosis answered 6/3, 2013 at 8:47 Comment(2)
I don't think filters can be applied on input field.Infield
Uppercase is all caps.Murmansk
J
-3

You could use pure css:

input { text-transform: capitalize; }

Jadajadd answered 20/8, 2013 at 20:31 Comment(2)
The OP only wants to capitalize the first letter in the input. This solution capitalizes each wordMerkel
@ChrisBier, I think you're confusing capitalize with uppercase. css-tricks.com/almanac/properties/t/text-transformLallans

© 2022 - 2024 — McMap. All rights reserved.