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
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>
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):
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.
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 }
}
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>
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/>