5

Please help. I'm having trouble creating a directive that will always set text inputs to uppercase. It seems to be working looking at the user interface but the model binding is showing the last typed character to still in lowercase character.

below is a portion of my html:

<div>
    <md-input-container fxFlex>
        <textarea #listCode mdInput [(ngModel)]="listInfo.code" placeholder="List Code" 
                  uppercase-code maxlength="50" rows="3"
                  required></textarea>
        <md-hint align="end">{{listCode.value.length}} / 50</md-hint>
    </md-input-container>
    {{listInfo.code}}
</div>

below is the directive:

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

@Directive({
  selector: '[ngModel][uppercase-code]',
  host: {
    '(ngModelChange)': 'ngOnChanges($event)'
  }
})
export class UppercaseCodeDirective {
  constructor(public model: NgControl) {}
  ngOnChanges(event) {
    var newVal = event.replace(/[^A-Za-z0-9_]*/g, '');
    newVal = newVal.toUpperCase();
    this.model.valueAccessor.writeValue(newVal);       
  }
}
JR S.
  • 53
  • 1
  • 3

2 Answers2

7

You should be using a directive as below,

@HostListener('keyup') onKeyUp() {
      this.el.nativeElement.value = this.el.nativeElement.value.toUpperCase();

    }

LIVE DEMO

Aravind
  • 40,391
  • 16
  • 91
  • 110
  • thank you for the reply but my problem is with the binding. I modified your plunker codes to show: [link](http://plnkr.co/edit/kKzD7TL0I7z391XwhIlO?p=preview). When I typed on the textbox, the displayed value at the right still have lowercase values. – JR S. Jun 10 '17 at 02:04
  • you have to handle it in the component and directive will not work in this case also it is a text area. – Aravind Jun 10 '17 at 02:10
  • I see, I'll try to create a custom component then. thanks! – JR S. Jun 10 '17 at 02:13
  • Yeah, it works better if it is being done within the component. Thanks! – JR S. Jun 10 '17 at 02:30
  • Yes. Though I am still searching if there's some hack to make it work with directives. I kinda already used my original approach all throughout my project.. [sigh] PS: I'm new here.. Still no rights to give public upvotes. – JR S. Jun 10 '17 at 04:34
  • This can be improved by using input instead of keyup so there isn't a visual delay of the text being converted to uppercase: @HostListener('input') onInput() { this.el.nativeElement.value = this.el.nativeElement.value.toUpperCase(); } – brett Dec 29 '20 at 20:43
1

This question has somehow already been answered on SO, here, although solutions piled up together with framework newer versions.

At least in my experience, there were two useful answers, which on their own did not work, anyway: 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.

superjos
  • 12,189
  • 6
  • 89
  • 134
  • 1
    In angular 4.4.1 (and maybe in previous version) Renderer is deprecated – Moussa Sep 19 '17 at 21:30
  • what's the purpose of LOWERCASE_INPUT_CONTROL_VALUE_ACCESSOR ? The directive works fine without it. – Moussa Sep 19 '17 at 21:56
  • @Mouss re. Renderer, very true. It's Renderer2 now, don't know exactly since which version. Re. value accessor (apart from being explained like that in blog posts) I believe it is needed so that your custom form participates to form validation/ dirty/ valid/ pristine/... logic **Edit** yep, see chapters on *Registering* [here](https://blog.thoughtram.io/angular/2016/07/27/custom-form-controls-in-angular-2.html) or [here](https://medium.com/@tarik.nzl/angular-2-custom-form-control-with-validation-json-input-2b4cf9bc2d73) – superjos Sep 20 '17 at 15:17