Angular 2 Directive to set input field to uppercase using ngModelChange
Asked Answered
C

2

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);       
  }
}
Cristal answered 10/6, 2017 at 1:41 Comment(0)
T
7

You should be using a directive as below,

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

    }

LIVE DEMO

Teryn answered 10/6, 2017 at 1:51 Comment(6)
thank you for the reply but my problem is with the binding. I modified your plunker codes to show: link. When I typed on the textbox, the displayed value at the right still have lowercase values.Cristal
you have to handle it in the component and directive will not work in this case also it is a text area.Teryn
I see, I'll try to create a custom component then. thanks!Cristal
Yeah, it works better if it is being done within the component. Thanks!Cristal
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.Cristal
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(); }Stratopause
C
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.

Congruity answered 2/8, 2017 at 0:7 Comment(3)
In angular 4.4.1 (and maybe in previous version) Renderer is deprecatedGummous
what's the purpose of LOWERCASE_INPUT_CONTROL_VALUE_ACCESSOR ? The directive works fine without it.Gummous
@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 or hereCongruity

© 2022 - 2024 — McMap. All rights reserved.