Recently, I asked the following question on StackOverflow and received an amazing response that resulted in building a recursive nested Angular Form: Angular Deeply Nested React
Stackblitz demo
There's nothing wrong with your data. It's likely a matter of timing. The form must be stable before you can patch any value. As you're doing it in OnInit
, I suppose you're building the form in the constructor. If so, the form isn't ready to receive any values yet.
The simplest way to solve that is with an uncomfortable setTimeout
. But I suppose that this is just for testing. In real cases, the data will probably get some time to be available anyway.
Your example above will work if you wrap it with the aforementioned setTimeout
:
ngOnInit() {
setTimeout(() => {
const data = {
statement: {
groups: [
{
conjunctor: "and",
conditions: [
{
variable: "asdf"
}
],
groups: []
}
]
}
};
this._form.patchValue(data);
console.log(this._form.value);
}, 50);
}
The animated gif below shows a patch in 3 nested groups, the last one being nested on the second one.
One interesting thing you can do is build the form automatically according to the data you have. You can achieve that by analyzing the data that gets into the ControlValueAccessor.writeValue
:
if (!value) {
return;
}
setTimeout(() => {
if (value && value.conditions && value.conditions.length) {
this._conditionsFormArray.clear();
value.conditions.forEach(c => this._addCondition());
}
if (value && value.groups && value.groups.length) {
this._groupsFormArray.clear();
value.groups.forEach(g => this._addGroup());
}
this._form.patchValue(value);
}, 50);
Maybe you've noticed the setTimeout
above, it's there because we have another setTimeout in the form creation method (called on OnInit
) that add a condition to the form after it's created, and the creation of the condition is, itself, inside a setTimeout
. Because of that, we must wait it runs, otherwise this._conditionsFormArray.clear();
wouldn't have any effect => no condition would've been created when it runs.
private _createFormGroup() {
this._form = this._fb.group({
conjunctor: null,
conditions: this._fb.array([]),
groups: this._fb.array([])
});
// add one condition on the next tick, after the form creation
setTimeout(() => this._addCondition());
}
Then with some adjustments, we can get to the following result (the gif below is based on this other Stackblitz demo):