I don't understand why every example I find on the internet has to be so complicated. When explaining a new concept, I think it's always best to have the most simple, working example possible. I've distilled it down a little bit:
HTML for external form using component implementing ngModel:
EmailExternal=<input [(ngModel)]="email">
<inputfield [(ngModel)]="email"></inputfield>
Self-contained component (no separate 'accessor' class - maybe I'm missing the point):
import {Component, Provider, forwardRef, Input} from "@angular/core";
import {ControlValueAccessor, NG_VALUE_ACCESSOR, CORE_DIRECTIVES} from "@angular/common";
const CUSTOM_INPUT_CONTROL_VALUE_ACCESSOR = new Provider(
NG_VALUE_ACCESSOR, {
useExisting: forwardRef(() => InputField),
multi: true
});
@Component({
selector : 'inputfield',
template: `<input [(ngModel)]="value">`,
directives: [CORE_DIRECTIVES],
providers: [CUSTOM_INPUT_CONTROL_VALUE_ACCESSOR]
})
export class InputField implements ControlValueAccessor {
private _value: any = '';
get value(): any { return this._value; };
set value(v: any) {
if (v !== this._value) {
this._value = v;
this.onChange(v);
}
}
writeValue(value: any) {
this._value = value;
this.onChange(value);
}
onChange = (_) => {};
onTouched = () => {};
registerOnChange(fn: (_: any) => void): void { this.onChange = fn; }
registerOnTouched(fn: () => void): void { this.onTouched = fn; }
}
In fact, I've just abstracted all of this stuff to an abstract class which I now extend with every component I need to use ngModel. For me this is a ton of overhead and boilerplate code which I can do without.
Edit: Here it is:
import { forwardRef } from '@angular/core';
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';
export abstract class AbstractValueAccessor implements ControlValueAccessor {
_value: any = '';
get value(): any { return this._value; };
set value(v: any) {
if (v !== this._value) {
this._value = v;
this.onChange(v);
}
}
writeValue(value: any) {
this._value = value;
// warning: comment below if only want to emit on user intervention
this.onChange(value);
}
onChange = (_) => {};
onTouched = () => {};
registerOnChange(fn: (_: any) => void): void { this.onChange = fn; }
registerOnTouched(fn: () => void): void { this.onTouched = fn; }
}
export function MakeProvider(type : any){
return {
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => type),
multi: true
};
}
Here's a component that uses it: (TS):
import {Component, Input} from "@angular/core";
import {CORE_DIRECTIVES} from "@angular/common";
import {AbstractValueAccessor, MakeProvider} from "../abstractValueAcessor";
@Component({
selector : 'inputfield',
template: require('./genericinput.component.ng2.html'),
directives: [CORE_DIRECTIVES],
providers: [MakeProvider(InputField)]
})
export class InputField extends AbstractValueAccessor {
@Input('displaytext') displaytext: string;
@Input('placeholder') placeholder: string;
}
HTML:
<div class="form-group">
<label class="control-label" >{{displaytext}}</label>
<input [(ngModel)]="value" type="text" placeholder="{{placeholder}}" class="form-control input-md">
</div>