Reputation: 228
I am currently working on reusable components with custom form field controls.
I have this custom autocomplete control that returns an object as its value, and then my reusable component, using this custom control, passes one of the object properties to my main form as a string. Here is a blitz to check it out:
https://stackblitz.com/edit/my-custom-autocomplete-jlkj9q
This works fine in one direction, control --> reusable-field --> form.
Here is how I update the form value from my reusable component:
ngOnInit(){
this.state = new FormControl(null);
// any time the form field value changes update the parent of any change
this.state.valueChanges
.pipe(takeUntil(this._destroy))
.subscribe(value => {
if(!!value && !!value.name){
this.onChange(value.name) //pass only the state.name to the ControlValueAccessor
this.onTouched();
}
else{
this.onChange(value)
this.onTouched();
}
});
}
Now, when I set a default value to the reusable component field, the control is updated correctly, but the form value is not. I tried calling the onChange()
method on the ngOnInit
with the value to be passed to the main form, but nothing happened. Then I tried to set the default value on the ngAfterViewInit and call the onChange
like this:
ngAfterViewInit(){
let defaultValue = {
"name": "Arkansas",
"population": "2.978M",
"flag": "https://upload.wikimedia.org/wikipedia/commons/9/9d/Flag_of_Arkansas.svg"
};
this.state.setValue(defaultValue);
this.onChange(defaultValue);
}
but this doesn't work either.
What am I missing here? Any clues?
Upvotes: 1
Views: 3698
Reputation: 46
For quick workaround add
setTimeout(() => this.state.setValue(this.defaultValue), 0);
at the end of ngOnInit
in reusable form field.
Upvotes: 2
Reputation: 29355
a couple of things...
your form group's default value is overriding the one provided by your form, the writeValue
method is called at the end of the ngOnInit
hook. You can get around this by simply calling setValue
in your reusable form in a later hook
you're using onPush change detection in your app component in your blitz. This means that only certain things will trigger change detection, and a child component updating a reactive form value is NOT one of them. This is why you're not seeing the change be reflected in your templates. This isn't a big deal if you're just interested in the form value and not in displaying it on screen, but it IS a big deal if you want to display it, as if you remove onPush change detection, you will begin seeing expression changed errors, as a child updating something in the parents template during initialization violates unidirectional data flow. Getting around this is a bit more difficult. The true answer here is that your parent form really should be the one providing the default value.
Upvotes: 0