You shouldn't use such implementation. It much more clean to use ControlValueAccessor.
ControlValueAccessor is an interface that allows to angular form module (classic or reactive) to write value or state and register callback to retrieve changes and event.
writeValue(value: Address): void { } // Allows angular to set a default value to the component (used by FormControl or ngModel)
registerOnChange(fn: (_: any) => void): void {} // Callback to be called when the component value change.
registerOnTouched(fn: (_: any) => void): void { } // Callback to be called when a "touch" event occurs on the component
setDisabledState(isDisabled: boolean): void { } // Allows angular to update the component disable state.
But you also need to provide a NG_VALUE_ACCESSOR
I wrote this quick and dirty example for an address component:
import { Component, forwardRef } from '@angular/core';
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';
import { Address } from './address';
@Component({
selector: 'app-address-input',
template: `
<label for="number">Num: </label>
<input type="number" [disabled]="disabled" name="number" id="number" (change)="numberUpdate($event)" value="{{value.num}}"/><br />
<label for="street">Street: </label>
<input type="text" [disabled]="disabled" (change)="streetUpdate($event)"name="street" id="street" value="{{value.street}}" /><br />
<label for="city">City: </label>
<input type="text" [disabled]="disabled" name="city" id="city" value="{{value.city}}" (change)="cityUpdate($event)" /><br />
<label for="zipCode">Zip Code: </label>
<input type="text" [disabled]="disabled" name="zipCode" id="zipCode" value="{{value.zipCode}}" (change)="zipCodeUpdate($event)" /><br />
<label for="state">State: </label>
<input type="text" [disabled]="disabled" name="state" id="state" value="{{value.state}}" (change)="stateUpdate($event)" /><br />
<label for="country">Country: </label>
<input type="text" [disabled]="disabled" name="country" id="country" value="{{value.country}}" (change)="countryUpdate($event)" />`,
providers: [{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => AddressInputComponent) // forward the reference,
multi: true // allow multiple component in the same form
}]
})
export class AddressInputComponent implements ControlValueAccessor {
private _onChange = (_: any) => {};
private _onTouched = (_: any) => {};
disabled = false;
private _value: Address = {num: undefined, street: undefined, city: undefined, state: undefined, zipCode: undefined, country: undefined}; // current value (Address is just an interface)
set value(value: Address) { // interceptor for updating current value
this._value = value;
this._onChange(this._value);
}
get value() {
return this._value;
}
writeValue(value: Address): void {
if (value && value !== null) {
this._value = value;
}
}
registerOnChange(fn: (_: any) => void): void {
this._onChange = fn;
}
registerOnTouched(fn: (_: any) => void): void {
this._onTouched = fn; // didn't used it but you should for touch screen enabled devices.
}
setDisabledState(isDisabled: boolean): void {
this.disabled = isDisabled;
}
numberUpdate(event: any) {
// additional check or process
this._updateValue(event.target.value, 'num')
}
streetUpdate(event: any) {
// additional check or process
this._updateValue(event.target.value, 'street')
}
cityUpdate(event: any) {
// additional check or process
this._updateValue(event.target.value, 'city')
}
zipCodeUpdate(event: any) {
// additional check or process
this._updateValue(event.target.value, 'zipCode')
}
stateUpdate(event: any) {
// additional check or process
this._updateValue(event.target.value, 'state')
}
countryUpdate(event: any) {
// additional check or process
this._updateValue(event.target.value, 'country');
}
private _updateValue(value: any, field: string) {
const newValue = this._value;
newValue[field] = value;
this.value = newValue;
}
}
Then in a form use it like any other form element:
<form [formGroup]="registerForm">
<app-address-input formControlName="address"></app-address-input>
</form>
You could add more logic into the component. Here is a working example. Please keep in mind it's a quick example and should be rework for cleaner code.
https://stackblitz.com/edit/angular-4dgxqh