I have an input
field mapped to an entity in my controller with a ngModel
2-way binding:
&
I just formed this solution after much research. It's kind of hacky since the Angular team doesn't recommend extending DefaultValueAccessor, but it automagically works for every input without having to mark each one individually.
import { Directive, forwardRef, Renderer2, ElementRef, Input } from '@angular/core';
import { DefaultValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';
export const VALUE_ACCESSOR: any = {
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => InputExtensionValueAccessor),
multi: true
};
@Directive({
selector: 'input:not([type]),input[type=text],input[type=password],input[type=email],input[type=tel],textarea',
host: {
'(input)': '_handleInput($event.target.value)',
'(blur)': 'onTouched()',
'(compositionstart)': '_compositionStart()',
'(compositionend)': '_compositionEnd($event.target.value)'
},
providers: [VALUE_ACCESSOR]
})
export class InputExtensionValueAccessor extends DefaultValueAccessor {
// HACK: Allows use of DefaultValueAccessor as base
// (https://github.com/angular/angular/issues/9146)
static decorators = null;
constructor(renderer: Renderer2, elementRef: ElementRef) {
super(renderer, elementRef, (null as any));
}
registerOnChange(fn: (_: any) => void): void {
super.registerOnChange(value => {
// Convert empty string from the view to null for the model
fn(value === '' ? null : value);
});
}
}
This is working great for me on Angular 4.4.5.