I ran into a issue using Angular 2 ngModel binding. plnkr
If I use ngModel to bind a value to a child component, the value is not populated on the OnInit function of the child component. So If I bind to a property call "boundName" and I try to access that in the OnInit, it will be null. However if I bind the to the same value in the parent control not using ngModel but a input parameter, the value is accessible in the OnInit function.
So.. if my parent component creates a child component like
<my-boundcomp [(ngModel)]="name" [(inputName)] ="name" ></my-boundcomp>
And my child components onInit Function is
public ngOnInit() {
console.log("Input Name :" + this.inputName);
console.log("Bound Name :" + this.boundName);
this._boundNameOnInit = this.boundName; // <--- Going to be null
this._inputNameOnInit = this.inputName; // <--- Going to be not null
}
I found this behavior to be odd, and unexpected. I'm not sure if this is a bug or I am not using the FormsModule ngModel correctly, but interesting enough that I thought I should share.
Here is the full plnkr https://plnkr.co/edit/Im5oz7q1HhG5MgGTTZ1R?p=preview
This code
[(ngModel)]="name"
sets the value of NgModel
in OnInit
not of BoundValueComponent
.
When BoundValueComponent.writeValue
is called from NgModel
, then boundName
is set.
I'm pretty sure this is as designed.
I believe this is a "problem" if you are dependent on the value that is set via writeValue when using the ControlValueAccessor. Since boundName is set and ngOnInit is one of the first things fired, the writeValue has not had a chance to run yet.
I tried adding this on some other lifecycle hooks (AfterViewInit, AfterContentInit) and it is still too early. In fact if you default your boundName to '' or something you will notice that in AfterViewInit or AfterContentInit, it is actually nulled out before the writeValue is called.
For this reason, I would suggest setting your value in the writeValue method. If you need it to be set only once, you can use a flag. See your code below...
export class BoundValueComponent implements OnInit, ControlValueAccessor {
@Input() inputName: string;
boundName: string;
private _boundNameOnInit : string;
private _inputNameOnInit : string;
private initialized: boolean = false;
private onTouchedCallback: () => void = noop;
private onChangeCallback: (_: any) => void = noop;
constructor() {
}
public ngOnInit() {
console.log("Input Name :" + this.inputName);
console.log("Bound Name :" + this.boundName);
// this._boundNameOnInit = this.boundName; // <--- Going to be null
this._inputNameOnInit = this.inputName; // <--- Going to be not null
}
get value(): any {
return this.boundName;
};
/**
* Set accessor including call the onchange callback.
*/
set value(v: any) {
if (v !== this.boundName) {
this.boundName = v;
this.onChangeCallback(v);
}
}
/**
* From ControlValueAccessor interface.
*/
writeValue(value: any) {
this.boundName = value;
if (!this.initialized) {
this._boundNameOnInit = this.boundName;
this.initialized = true;
}
}
.......
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With