Reputation: 3341
I have created a custom directive to remove whitespaces from the input box which works completely fine till Angular version 7, but not working in Angular version 9.
import {
Directive,
ElementRef,
Output,
EventEmitter,
} from '@angular/core';
@Directive({
selector: '[trim]',
host: {
'(blur)': 'onBlur()'
}
})
export class TrimDirective {
@Output() ngModelChange: EventEmitter < any > = new EventEmitter();
constructor(private element: ElementRef) {}
onBlur() {
(this.element.nativeElement as HTMLInputElement).value = (this.element.nativeElement as HTMLInputElement).value.trim();
this.ngModelChange.emit((this.element.nativeElement as HTMLInputElement).value.trim());
}
}
On blur event it supposed to trim the whitespaces and update the ngModel, but it's not updating ngModel
Upvotes: 3
Views: 9496
Reputation: 3341
I know this is pretty late, but It's been around 3 years since I posted this question, and the answer given by radik is working well with template-driven forms but with Reactive forms, it is not working as expected. It trims the HTML control value, but it does not update the corresponding form control. So I'm posting this answer which works with Template driven forms as well as with reactive forms.
trim.directive.ts
looks like follows
import { Directive, ElementRef, EventEmitter, HostListener, Output } from '@angular/core';
import { NgControl } from '@angular/forms';
@Directive({
selector: '[trim]'
})
export class TrimDirective {
@Output() ngModelChange = new EventEmitter();
constructor(private el: ElementRef,
private control: NgControl) {
}
/**
* Trim the input value on focus out of input component
*/
@HostListener('focusout') onFocusOut() {
(this.el.nativeElement as HTMLInputElement).value = (this.el.nativeElement as HTMLInputElement).value.trim();
this.ngModelChange.emit(this.el.nativeElement.value)
this.control.control?.setValue(this.el.nativeElement.value)
}
}
And just use the trim directive in your HTML as below.
<input trim matInput name="name" id="name" formControlName="name"/>
This will not just update the HTML control it updates the angular form control as well and it emits the trimmed value on the ngModelChange event.
Upvotes: 8
Reputation: 39
Directive file:
import { Directive, ElementRef, HostListener } from '@angular/core';
@Directive({
selector: '[appTrim]',
})
export class TrimDirective {
debugger;
constructor(
private el: ElementRef
) { }
@HostListener('blur') onBlur() {
const value = this.el.nativeElement.value;
if (value) {
this.el.nativeElement.value = value.trim();
}
}
}
Html use:
<input appTrim id="coxid52" type="text" class="form-control" ... />
Make sure to import that directive into the respective module as well.
Upvotes: 0
Reputation: 1
@Directive({
selector: '[formControl], [formControlName]',
})
export class TrimDirective {
constructor(private readonly control: NgControl) {}
@HostListener('blur')
onBlur(): void {
if (typeof this.control.value === 'string') {
this.control.control.setValue(this.control.value.trim());
}
}
}
Upvotes: 0
Reputation: 2629
Here is how we have implemented it, working from Angular 4.x to 11.x
As you will see, we are using selectors so that the directive is applied to all input / textarea elements without having to explicitly use the directive.
This can be easily changed and instead use the "app-trim"
import { Directive, HostListener, forwardRef } from "@angular/core";
import { DefaultValueAccessor, NG_VALUE_ACCESSOR } from "@angular/forms";
const TRIM_VALUE_ACCESSOR: any = {
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => TrimValueDirective),
multi: true
};
/**
* The trim accessor for writing trimmed value and listening to changes that is
* used by the {@link NgModel}, {@link FormControlDirective}, and
* {@link FormControlName} directives.
*/
@Directive({
selector: `
input:not([type=checkbox]):not([type=radio]):not([type=password]):not([readonly]):not(.app-trim-ignore)[formControlName],
input:not([type=checkbox]):not([type=radio]):not([type=password]):not([readonly]):not(.app-trim-ignore)[formControl],
input:not([type=checkbox]):not([type=radio]):not([type=password]):not([readonly]):not(.app-trim-ignore)[ngModel],
textarea:not([readonly]):not(.app-trim-ignore)[formControlName],
textarea:not([readonly]):not(.app-trim-ignore)[formControl],
textarea:not([readonly]):not(.app-trim-ignore)[ngModel],
:not([readonly]):not(.app-trim-ignore)[ngDefaultControl],
[app-trim]
`,
providers: [TRIM_VALUE_ACCESSOR]
})
export class TrimValueDirective extends DefaultValueAccessor {
@HostListener("input", ["$event.target.value"])
ngOnChange = (val: string): void => {
this.onChange(val.trim());
};
@HostListener("blur", ["$event.target.value"])
applyTrim(val: string): void {
this.writeValue(val.trim());
}
writeValue(value: any): void {
if (typeof value === "string") {
value = value.trim();
}
super.writeValue(value);
}
}
Upvotes: 6
Reputation: 2795
to update input value they use both setProperty() and setAttribute()
import { Directive, EventEmitter, Input, ChangeDetectorRef, Output, ElementRef, HostListener, Inject, Renderer2 } from '@angular/core';
import { NgModel } from '@angular/forms';
@Directive({
selector: '[appTrim]'
})
export class TrimDirective {
constructor(
private renderer: Renderer2,
private elementRef: ElementRef,
private ngModel: NgModel) { }
@HostListener("blur")
onBlur() {
let value = this.ngModel.model;
if(value) {
value = value.trim();
this.renderer.setProperty(
this.elementRef.nativeElement, "value", value);
this.renderer.setAttribute(
this.elementRef.nativeElement, "value", value);
this.ngModel.update.emit(value);
} else {
this.renderer.setProperty(
this.elementRef.nativeElement, "value", null);
this.renderer.setAttribute(
this.elementRef.nativeElement, "value", null);
this.ngModel.update.emit("");
}
}
}
example link
Upvotes: 3