9

I want to use a directive to transform all input data to uppercase. To achieve that, I create this custom directive :

@Directive({
  selector: '[appToUpperCase]'
})
export class ToUpperCaseDirective {

  constructor() {}

  @HostListener('input', ['$event']) onKeyUp(event) {
    event.target['value'] = event.target['value'].toUpperCase();
  }

}

And I use it like that :

<form [formGroup]="formGroup" appToUpperCase>

It works almost good exept that when I enter text in my field, the upper case transform is permormed but the focus is set at the end of the field...So when I edit a pre filled input, if I want to modify the begining of the data, I have to set the focus at the right place after each Keyup event...

How can I fix that ?

Julien AZEMA
  • 186
  • 1
  • 2
  • 9

8 Answers8

10

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.

// user.component.html
<input  type="text" name="name" class="form-control" formControlName="name" uppercase />


// user.component.ts
import { Directive, ElementRef, HostListener } from '@angular/core';
@Directive({
selector: '[uppercase]',
  host: {
    '(input)': '$event'
  }
})
export class UppercaseInputDirective {

  lastValue: string;

  constructor(public ref: ElementRef) { }

  @HostListener('input', ['$event']) onInput($event) 
  {
    var start = $event.target.selectionStart;
    var end = $event.target.selectionEnd;
    $event.target.value = $event.target.value.toUpperCase();
    $event.target.setSelectionRange(start, end);
    $event.preventDefault();

    if (!this.lastValue || (this.lastValue && $event.target.value.length > 0 && this.lastValue !== $event.target.value)) {
      this.lastValue = this.ref.nativeElement.value = $event.target.value;
      // Propagation
      const evt = document.createEvent('HTMLEvents');
      evt.initEvent('input', false, true);
      event.target.dispatchEvent(evt);
    }
  }
}

I posted here in stackblitz

heavyrick
  • 384
  • 7
  • 16
9

For reactive form inputs (and also textareas) I got it working just with:

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

@Directive({
  selector: '[formControlName][inputUppercase]'
})
export class InputUppercaseDirective {

  constructor(
    private readonly control: NgControl
  ) { }

  @HostListener('input', ['$event.target'])
  public onInput(input: HTMLInputElement): void {
    const caretPos = input.selectionStart;
    this.control.control.setValue(input.value.toUpperCase());
    input.setSelectionRange(caretPos, caretPos);
  }
}
José Antonio Postigo
  • 2,674
  • 24
  • 17
5

Basically when you modify the value from imperative code, it becomes difficult manage position of cursor. When you re-write value input value, it throws cursor at the start of input.

I'd recommend you to go for CSS way. More cleaner

[my-attribute] input { text-transform: uppercase; }
Pankaj Parkar
  • 134,766
  • 23
  • 234
  • 299
  • 2
    The problem is that CSS display the data in upper case but don't transform the value. I have many pages with many forms so I want a simple way to transform the incoming data to uppercase for the display and in the model. It s the reason why I used a Directive – Julien AZEMA Oct 19 '17 at 08:00
  • 2
    @JulienAZEMA I'd go with this one, you get the visual appearance and seems you have a form, so what you could do is that upon submit you can iterate the form object properties and transform them to uppercase :) – AT82 Oct 20 '17 at 10:34
  • 1
    this solutions is simple and works for me ⭐⭐⭐⭐⭐ – C Alonso C Ortega Aug 20 '22 at 04:22
1

Pankaj's idea it's better than create a directive. You only must send data.toUpperCase(). Anyway if you want use a directive like this. Be careful, not only we must use preventDefault(), we must dispatch event change

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

@Directive({
  selector: '[appToUpperCase]'
})
export class ToUpperCaseDirective {

  constructor() { }

  @HostListener('keydown', ['$event']) onKeyDown(event:KeyboardEvent) {
    if (event.keyCode>32 && event.keyCode<128)
   {
      event.target['value']+=event.key.toUpperCase();
      event.preventDefault(); //stop propagation
      //must create a "input" event, if not, there are no change in your value
      var evt = document.createEvent("HTMLEvents");
      evt.initEvent("input", false, true);
      event.target.dispatchEvent(evt);
    }

  }

}
Eliseo
  • 50,109
  • 4
  • 29
  • 67
  • 1
    event.keyCode>32 && event.keyCode<128 is a bad idea. If you press del, keyup etc. Check codes on this website : https://keycode.info/ I prefer use onInput event. – Leasye Nov 05 '18 at 14:38
0
import { Directive, ElementRef, HostListener } from '@angular/core';

@Directive({
  selector: '[lowercaser]'
})
export class LowerCaserDirective {

  lastValue: string;

  constructor(public ref: ElementRef) {
  }

  @HostListener('input', ['$event']) onInput(event) {
    const resEventValue = event.target.value.toLowerCase();
    // Avoid max call
    if (!this.lastValue || (this.lastValue && event.target.value.length > 0 && this.lastValue !== resEventValue)) {
      this.lastValue = this.ref.nativeElement.value = resEventValue;
      // Propagation
      const evt = document.createEvent('HTMLEvents');
      evt.initEvent('input', false, true);
      event.target.dispatchEvent(evt);
    }
  }

}
Leasye
  • 261
  • 1
  • 8
  • 19
  • Works with TD form (Angular 7). Please check this deprecation on MDN about events: https://developer.mozilla.org/en-US/docs/Web/API/Event/initEvent – robert Dec 26 '18 at 17:03
0
<input class="form-control form-control-sm" id="area" type="text" formControlName="carea" #carea (keyup)="makeUpperCase(addform.get('carea'))"


makeUpperCase(control: FormControl) {
control.setValue(control.value.toUpperCase());

}

make a function in your util.servce.ts or component.ts, take form control as an input, setValue for that control as mentioned in the code.

Call this function from your component.html as shown.

Aady
  • 56
  • 1
  • 7
0

Upgrading @Leasye answer due to the deprecation of initEvent as mentioned by @robert I have used Event class to create the event. I've also used setProperty() method of Renderer2 to assign value to input element, rather than assigning it to the DOM object directly.

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

@Directive({
  selector: '[appTextUppercase]'
})
export class TextUppercaseDirective {
  preValue: string = "";

  constructor(private elmRef: ElementRef, private renderer: Renderer2) { }

  @HostListener('input', ['$event']) onInput(event: any) {
    const text_upper = event.target.value.toUpperCase();
    if (!this.preValue || (this.preValue && text_upper && this.preValue !== text_upper)) {
      this.preValue = text_upper;
      this.renderer.setProperty(this.elmRef.nativeElement, 'value', text_upper);
      const htmlEvent = new Event('input', { "bubbles": false, "cancelable": true         
    });
        document.dispatchEvent(htmlEvent);
        }
    };
}

However, would like to point to an issue in the above directive, that it does not bind the last edited character in case of editing the field value by moving the cursor back. In which case solution by @Andy works fine.

0

Upgrading @Amit answer to solve issue, that directive does not bind the last edited character, you can use target.dispatchEvent(htmlEvent) instead of document.dispatchEvent(htmlEvent). It helped me to solve this problem.