30

I am trying to validate the input fields using ngControl's value in angular 2. i need to validate that the user enters the value in upper case always.

Now we need to convert the value entered by user to uppercase. But i am handling values from input fields using ngControl, not ngModel ( considering i could have used ngModelChange event to update value to uppercase.)

So what is the best and low cost way to convert the value used by ngControl.

Fred
  • 3
  • 1
ankitkamboj
  • 531
  • 2
  • 6
  • 14

10 Answers10

39

As @Eric Martinez suggested, you can create a local template variable, and bind the uppercase string to the value property on the input event:

<input type="text" #input (input)="input.value=$event.target.value.toUpperCase()" />

Alternatively, you can make this a directive:

@Directive({
    selector: 'input[type=text]',
    host: {
        '(input)': 'ref.nativeElement.value=$event.target.value.toUpperCase()',
    }

})
export class UpperCaseText {
    constructor(private ref: ElementRef) {
    }
}

To use the directive, specify UpperCaseText in your component's list of directives:

directives: [UpperCaseText]

Demo Plnkr

Michael Kang
  • 52,003
  • 16
  • 103
  • 135
  • This will give an error if implemented in beta 9. 'Value changed after it was last checked' –  Mar 24 '16 at 19:54
  • I still get it. I've fixed it by not doing it at all since (ngModelChange) doesn't work either. –  Mar 25 '16 at 18:40
  • Are you sure it's the above code that's causing the issue? I'm not getting any errors – Michael Kang Mar 25 '16 at 22:37
  • this line doesn't work: `` –  Mar 27 '16 at 23:52
  • Seems to work fine. Here is a plnkr: http://plnkr.co/edit/uq6qxHHVxOl3M0aVCCBG?p=preview – Michael Kang Mar 28 '16 at 19:40
  • It also works with double binding great: http://plnkr.co/edit/EmfpsND4q1ozsPQ6wene?p=preview –  Oct 19 '16 at 15:51
  • I attempted the first method in an `[(ngModel)]` template type form and the last character is not upperCased, *despite* the value being *visually uppercase* in the input field (`document.getElement...().value` shows the true value). There are no other dynamic input or onChange handlers. Using angular2/core@2.2.4. – msanford Dec 05 '16 at 14:46
  • it's working for me. Thanx. whatever I enter in the text box has to change as upper case. it'w working Angular CLI projects. May be you missed import { FormControl } from '@angular/forms' ur compoents. – gnganapath Feb 09 '17 at 11:12
  • 2
    `input.value=$event.target.value.toUpperCase()"` in this implementation if the last character is in lowercase. it remains as it is. angular 4.3 – xkeshav Sep 28 '18 at 08:35
25

Here is my solution:

Using host listener to listen input event and then force it to uppercase.

import {Directive, EventEmitter, HostListener, Output} from '@angular/core';
@Directive({
  selector: '[ngModel][uppercase]'
})
export class UppercaseDirective {
  @Output() ngModelChange: EventEmitter<any> = new EventEmitter();
  value: any;

  @HostListener('input', ['$event']) onInputChange($event) {
    this.value = $event.target.value.toUpperCase();
    this.ngModelChange.emit(this.value);
  }
}

With this directive, you can easily force input to uppercase like this:

<input type="text" class="form-control" placeholder="ID"
           formControlName="id" [(ngModel)]="form.value.id" uppercase/>
subaru710
  • 567
  • 4
  • 4
  • 1
    Simple, elegant, effective, no hassles. The order of the selectors does not matter, so they can be reversed if you need to comply with TS linting against Angular standards. This should be the accepted answer IMO. Well done! – MBielski Sep 28 '17 at 14:20
  • Worked like charm with reactive form . – I.Tyger Nov 25 '17 at 12:57
  • 1
    Mine didn't work, what did I forget? 1) created the class, 2) imported on my SharedModule.ts, 3) imported SharedModule.ts on MyPage.ts and set the field just like in the example above. – Francisco Souza Dec 09 '17 at 16:00
  • 1
    @franzisk_br Did you export the directive from SharedModule? – subaru710 Dec 13 '17 at 07:29
  • how does `this.value` bind with the data in the form? I don't see where that happens – CodyBugstein Mar 08 '18 at 20:09
  • 2
    the last character it's lowercased, how can i fix this? (i'm using angular 6) – heavyrick Oct 19 '18 at 21:08
  • 1
    I developed a solution in Angular 7 for uppercase and lowercase, based in some posts i've read. But i tested only for reactive forms. This solution resolve the problem of the last word and the position of the cursor. https://stackblitz.com/edit/angular-form-uppercase-lowercase – heavyrick Jan 11 '19 at 10:55
  • @subaru710 if we are intercepting input event on the host, should we be $event.preventDefault(); $event.stopPropagation(); ? – Rov Aug 27 '20 at 16:07
12
<input type="text" oninput="this.value = this.value.toUpperCase()">
works good in angular to get every symbol to be a big one :)
9

I would create a custom implementation of ControlValueAccessor. The latter would correspond to a directive that would listen the input event of the host. This way you will be able to put in uppercase what you user fills. The control will automatically contains the value in uppercase.

Here is the implementation:

@Directive ({
  selector: 'input[uppercase]',
  // When the user updates the input
  host: { '(input)': 'onChange($event.target.value.toUpperCase())' }
})
export class UppercaseValueAccessor extends DefaultValueAccessor {
  (...)

  // When the code updates the value of the
  // property bound to the input
  writeValue(value:any):void {
    if (value!=null) {
      super.writeValue(value.toUpperCase());
    }
  }
}

Don't forget to register this custom value accessor in the directive providers. This way your custom value accessor will be used instead of the default one.

const UPPERCASE_VALUE_ACCESSOR = new Provider(NG_VALUE_ACCESSOR, { useExisting: forwardRef(() => UppercaseValueAccessor), multi: true});

@Directive ({
  providers: [ UPPERCASE_VALUE_ACCESSOR ],
  (...)
})
export class UppercaseValueAccessor ...

And add the directive in the directives attribute of the component where you want to use this approach.

See this class for more details:

This link could give additional hints (see section "NgModel-compatible component):

Thierry Templier
  • 198,364
  • 44
  • 396
  • 360
  • 3
    In order to to make it work with validation (`[class.invalid]="myControl.touched && !myControl.valid`), in response to the input event I needed to call `super.writeValue(updatedValue); this.onTouched(); this.onChange(updatedValue);` – cghislai Apr 13 '16 at 15:15
  • [DefaultValueAccessor source](https://github.com/angular/angular/blob/4.3.2/packages/forms/src/directives/default_value_accessor.ts) as of 4.3.2 – superjos Aug 01 '17 at 23:21
9

At least in my experience, I found two of the answers here insightful, but not working on their own: from Thierry Templier (with first comment as well), and from cal.

I put together parts of both, and came up with this version, which is now working with Angular 4.1.1 in a reactive form:

import { Directive, Renderer, ElementRef, forwardRef } from '@angular/core';
import { NG_VALUE_ACCESSOR, DefaultValueAccessor } from '@angular/forms';

const LOWERCASE_INPUT_CONTROL_VALUE_ACCESSOR = {
  provide: NG_VALUE_ACCESSOR,
  useExisting: forwardRef(() => LowerCaseInputDirective),
  multi: true,
};

@Directive({
  selector: 'input[lowercase]',
  host: {
    // When the user updates the input
    '(input)': 'onInput($event.target.value)',
    '(blur)': 'onTouched()',
  },
  providers: [
    LOWERCASE_INPUT_CONTROL_VALUE_ACCESSOR,
  ],
})
export class LowerCaseInputDirective extends DefaultValueAccessor {

  constructor(renderer: Renderer, elementRef: ElementRef) {
    super(renderer, elementRef, false);
  }

  writeValue(value: any): void {
    const transformed = this.transformValue(value);

    super.writeValue(transformed);
  }

  onInput(value: any): void {
    const transformed = this.transformValue(value);

    super.writeValue(transformed);
    this.onChange(transformed);
  }

  private transformValue(value: any): any {
    const result = value && typeof value === 'string'
      ? value.toLowerCase()
      : value;

    return result;
  }
}

This is for lower-case, but everything holds for upper-case as well, just rename directive, replace within selector and transformValue.

Edit:
A straightforward usage example from HTML code using such directive:

<input id="myField"
       formControlName="myField"
       type="text" class="form-control required" 
       lowercase>
superjos
  • 12,189
  • 6
  • 89
  • 134
  • I was happy to find Your solution @superjos but I do not get it to work in combination with the [formControlName] directive on the same input element. Have you gotten that to work? – Johan Sundström Oct 24 '17 at 11:44
  • 1
    Glad it helps somehow. Yes it does work, just added an usage example. Just double checking: did you actually add directive to the list of declared and/or exported components in some module? – superjos Oct 25 '17 at 10:26
  • @superjos I used your approach in Angular 8, I could not use host binding in decorator, so I used host listener instead, it worked like a charm :) on a side note, if we use a host listener to capture input event, should we stop propagation and prevent default $event.preventDefault(); $event.stopPropagation(); – Rov Aug 27 '20 at 16:05
  • Sorry I cannot help on this, I've been far from this specific bit of angular for some time – superjos Aug 28 '20 at 17:06
5

pixelbits has provided a great solution but it does not work in the latest version of Angular (v4.3.1) as directives are depreciated from component. My solution is based on his answer only but works with the latest

I am providing a generic solution with custom attribute directive with a boolean input which will covert the input to Uppercase if it is true.

upper-case.directive.ts :

     import { Directive, ElementRef, Input } from '@angular/core';
     @Directive({
     selector: '[UpperCase]',
     host: {
        '(input)': 'toUpperCase($event.target.value)',

     }

    })
    export class UpperCaseTextDirective  {

    @Input('UpperCase') allowUpperCase: boolean;
    constructor(private ref: ElementRef) {
    }

    toUpperCase(value: any) {
        if (this.allowUpperCase)
        this.ref.nativeElement.value = value.toUpperCase();
    }

    }

Here is the corresponding App component with the template.

app.ts

    //our root app component
   import {Component, NgModule, VERSION} from '@angular/core'
   import {BrowserModule} from '@angular/platform-browser'
   import {UpperCaseTextDirective} from './upper-case.directive'

    @Component({
    selector: 'my-app',
    template: `
    <div>
      <h2>Hello {{name}}</h2>
      Auto Capitalize True: <input [UpperCase]="true" type="text" #input />
    <br/>
     Auto Capitalize False: <input [UpperCase]="allowEdit" type="text"/>

    </div>
    `,
    })
    export class App {
    name:string;
    allowEdit:boolean;
    constructor() {
    this.name = `Angular! v${VERSION.full}`;
    this.allowEdit= false;
    }
     }

     @NgModule({
    imports: [ BrowserModule ],
    declarations: [ App,UpperCaseTextDirective ], 
    bootstrap: [ App ]
    })
   export class AppModule {}

Here is a Plnkr which demonstrate this.

KIA
  • 182
  • 2
  • 9
2

Simple code without directives

In the blur event from your Input text call a method that changes the value to upper case, mine is called "cambiaUpper"

<input id="shortsel" type="text" class="form-control m-b-12" #shortsel="ngModel" name="shortsel" [(ngModel)]="_stockprod.shortName" (blur)="cambiaUpper($event)"/>

And in the component (yourComponentFile.ts) create this method that receives the event, get the value from the event and change this to uppercase.

public cambiaUpper(event: any) {
      event.target.value = event.target.value.toUpperCase();
}

Tada!

2

In the blur event from your input text will changes the value to uppercase

<input type="text" id="firstName" name="firstName" (blur)="$event.target.value = $event.target.value.toUpperCase()">
j3ff
  • 5,719
  • 8
  • 38
  • 51
1

Here's my more generic solution which is basically like DefaultValueAccessor with a text "transformer" function added. So you would use

<input mdInput [transformer]="uppercase" ...>

In your compontent you have the uppercase function (you could do other things beside uppercase like implement a mask)...

  uppercase(value: string) {
    return value.toUpperCase();
  }

Directive...

import { NG_VALUE_ACCESSOR, ControlValueAccessor } from '@angular/forms';
import { Directive, forwardRef, Input, OnChanges, SimpleChanges, Renderer, ElementRef } from '@angular/core';
import { TextMaskModule, MaskedInputDirective } from 'angular2-text-mask';

@Directive({
  selector: 'input[transformer]',
  // When the user updates the input
  host: { '(input)': 'handleInput($event.target.value)', '(blur)': 'onTouched()' },
  providers: [
    { provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => TextTransformerDirective), multi: true },
  ]

})
export class TextTransformerDirective implements ControlValueAccessor {
  private inputElement: HTMLInputElement
  lastValue = "";
  onTouched = () => { }
  onChange = (_: any) => { }
  @Input('transformer')
  transformer = (v: string) => v;

  constructor(private renderer: Renderer, private element: ElementRef) {

  }

  handleInput(value: any) {
    let newVal = this.transformer(value);
    if (newVal != value || this.lastValue != newVal) {
      this.lastValue = newVal;
      this.renderer.setElementProperty(this.element.nativeElement, 'value', newVal);
      this.onChange(newVal);
    }
  }

  writeValue(value: any) {
    let normalizedValue = value == null ? '' : value;
    normalizedValue = this.transformer(normalizedValue);
    this.renderer.setElementProperty(this.element.nativeElement, 'value', normalizedValue);
  }

  registerOnChange(fn: (value: any) => any): void { this.onChange = fn }

  registerOnTouched(fn: () => any): void { this.onTouched = fn }

}
Cal
  • 359
  • 3
  • 5
  • 1
    It seems to me that a lot of pieces are just the same as `DefaultAccessor`. Why not inherithing from that, and adding only what's different? – superjos Aug 01 '17 at 23:50
0

Here is my working code i am using angular4

This is your directive for upper case

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

@Directive({
  selector: '[appUpper]'
})
export class UpperDirective {

  constructor(public ref: ElementRef) { }

  @HostListener('input', ['$event']) onInput(event) {
    this.ref.nativeElement.value = event.target.value.toUpperCase();
  }

}

This is your html file code where you used uppercase directive

<input type="text" id="id" placeholder="id" tabindex="0" formControlName="id" appUpper>
  • 3
    This works great, but there is one small issue if I have this on template driven form once I submit the form I get the value all uppercase except the last character. So if I type: 'aaa' this directive does the transformation to 'AAA' but once I submit I got the value as 'AAa' <- last letter is not transformed. Any Idea? (Angular 6) – robert Aug 29 '18 at 19:53
  • @robert did you fix it? – Alexander Rojas Sep 09 '18 at 03:03
  • @AlexanderRojas This looks promising: https://stackoverflow.com/a/46832182/2050306 – robert Sep 10 '18 at 21:07