Set null as empty string value for input field

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.


After viewing a bunch of answers about ValueAccessor and HostListener solutions, I made a working solution (tested with RC1):

import {NgControl} from "@angular/common";
import {Directive, ElementRef, HostListener} from "@angular/core";

@Directive({
  selector: 'input[nullValue]'
})
export class NullDefaultValueDirective {
  constructor(private el: ElementRef, private control: NgControl) {}

  @HostListener('input', ['$event.target'])
  onEvent(target: HTMLInputElement){
    this.control.viewToModelUpdate((target.value === '') ? null : target.value);
  }
}

Then use it that way on your input fields:

<input [(ngModel)]="bindedValue" nullValue/>