1

We've built a rather fancy dynamic component creation framework to build out complex forms based on json data. We have components that get created via

const questionComponent = FieldComponentMap[childField.shortName];    
viewContainerRef.createComponent<QuestionComponent>(questionComponent);

Those components may create child components of their own, etc, etc. It's all very elegant, and we like the design. We've got the whole thing "working", but it's not working well. Child, or perhaps grandchild components often don't render the first time around until something changes on the page. I'm sure we're just missing something simple, but we could use some more eyes to help us find what we're doing wrong.

We've tried moving all the dynamic component creation out of ngOnInit and into ngAfterContentInit, but that didn't help. We obviously want the complete form to render immediately.

Here's a StackBlitz demonstrating our issue (drastically simplified from our framework)... StackBlitz

3
  • I don't know if there is a problem with the StackBlitz you created but if you look at the console, there are a few error beign thrown Commented Jul 6, 2022 at 18:41
  • We've had a devil of a time making sense of those errors and why the form actually starts to work when the form is interacted with. It's like the errors only happen at that point in time, and at another point, they go away? Commented Jul 6, 2022 at 19:37
  • So those are not 'StackBlitz' errors? you are getting those same errors on your development environment as well? Commented Jul 6, 2022 at 21:00

2 Answers 2

2

I think you're lost in the hierarchy.

To be more precisely, you're operating a wrong instance of FormGroup in

array-question-wrapper.component.ts

Try replacing

this.createChildQuestionComponents(this.formGroup);

with

this.createChildQuestionComponents(this.formGroup.get(
     [this.formArrayName, this.formArrayIndex]) as FormGroup);

Forked Stackblitz

Another option

When working with dynamically creating forms you don't need to wrap all your ng-templates with formGroup, formArrayName directives. They are not tied to your child dynamically created controls anyway.

So:

parent-array-question.component.ts

Replace

componentRef.instance.containingControl = this.formGroup;

with

componentRef.instance.containingControl = arrayItem;

array-question-wrapper.component.ts

template should be:

<div class="child-questions">
  <ng-template appQuestionHost></ng-template>
</div>

or just <ng-template appQuestionHost></ng-template> if you don't need any additional classes here or you can use host element to style it.

Forked Stackblitz 2

Sign up to request clarification or add additional context in comments.

1 Comment

Excellent job finding the problem. We found it as well just a few hours before you posted this. I was going to write up a solution, but you've done a great job. We were passing the wrong FormGroup from ParentArrayQuestionComponent to ArrayQuestionWrapperComponent. I definitely want to look at your Option 2, but I haven't had time yet. I find it a pain having to rebuild the hierarchy in the templates, especially for child components inside FormArrays.
0

I had issues with template portals that were instantiated within a component that was dynamically added. Setting a timeout to associate the template portal within the dynamically created component fixed it. I am not perfectly satisfied with that. But, it may give a hint as to why your controls are not being recognised.

1 Comment

Your answer could be improved with additional supporting information. Please edit to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers in the help center.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.