title: Angular 6 Dynamic Complex Model-Driven Reactive Forms
body: <p>I'm trying to create a form which is built dynamically according to a certain model using Angular's reactive forms. The model is a complex object <strong>which can change</strong>, and contains nested objects and values, for example (in TypeScript notation):</p>
  <h3>model.d.ts</h3>
  <pre class="lang-ts prettyprint-override"><code>interface Model {
      // ... other stuff
      config: { // complex object which can change, here's an example for some structure
          database: {
              uri: string;
              name: string;
          };
          server: {
              endpoint: {
                  host: string;
                  port: number;
              };
              requestTimeoutMs: number;
          };
      };
  }
  </code></pre>
  <p>Using this object, I built a <code>FormGroup</code> like so:</p>
  <h3>form.component.ts</h3>
  <pre class="lang-ts prettyprint-override"><code>@Component({
      // ...
      templateUrl: './form.component.html'
  })
  export class FormComponent {
      @Input() model: Model;
      public form: FormGroup;
  
      // instantiating the whole form
      public ngOnInit(): void {
          this.form = new FormGroup({
              // ... instantiating other controls using the model,
              config: new FormGroup({}),
          });
          // building the config form using model.config which is dynamic
          this.buildForm(this.form.get('config') as FormGroup, this.model.config);
      }
  
      // helper functions used in the template
      public isGroup(control: AbstractControl): control is FormGroup {
          return control instanceof FormGroup;
      }
      public typeOf(value: any): string {
          return typeof value;
      }
  
      // building the config form
      private buildForm(group: FormGroup, model: object): void {
          for (const key in obj) {
              const prop = obj[key];
              let control: AbstractControl;
              if (prop instanceof Object) { // nested group
                  control = new FormGroup({});
                  this.buildForm(control as FormGroup, prop);
              } else { // value
                  control = new FormControl(prop);
              }
              group.addControl(control);
          }
      }
  }
  </code></pre>
  <p>This code works as expected and creates the form according to the object. I know this doesn't check for arrays and <code>FormArray</code>s, because I don't use them currently.</p>
  <p>After building the form, I'm trying to display the whole form in my template, which is where I've got a problem. This is my template:</p>
  <h3>form.component.html</h3>
  <pre class="lang-html prettyprint-override"><code>&lt;form [ngForm]=&quot;form&quot; (ngSubmit)=&quot;...&quot;&gt;
      &lt;!-- ... other parts of the form --&gt;
      &lt;ng-container [ngTemplateOutlet]=&quot;formItemTemplate&quot;
                    [ngTemplateOutletContext]=&quot;{parent: form, name: 'config': model: model; level: 0}&quot;&gt;
      &lt;/ng-container&gt;
      &lt;ng-template #formItemTemplate let-parent=&quot;parent&quot; let-name=&quot;name&quot; let-model=&quot;model&quot; let-level=&quot;level&quot;&gt;
          &lt;ng-container [ngTemplateOutlet]=&quot;isGroup(parent.get(name)) ? groupTemplate : controlTemplate&quot;
                        [ngTemplateOutletContext]=&quot;{parent: parent, name: name, model: model, level: level}&quot;&gt;
          &lt;/ng-container&gt;
      &lt;/ng-template&gt;
      &lt;ng-template #groupTemplate let-parent=&quot;parent&quot; let-name=&quot;name&quot; let-model=&quot;model&quot; let-level=&quot;level&quot;&gt;
          &lt;ul [formGroup]=&quot;parent.get(name)&quot;&gt;
              &lt;li&gt;{{name | configName}}&lt;/li&gt;
              &lt;ng-container *ngFor=&quot;let controlName of parent.get(name) | keys&quot;&gt;
                  &lt;ng-container [ngTemplateOutlet]=&quot;formItemTemplate&quot;
                                [ngTemplateOutletContext]=&quot;{name: controlName, model: model[name], level: level + 1}&quot;&gt;
                  &lt;/ng-container&gt;
              &lt;/ng-container&gt;
          &lt;/ul&gt;
      &lt;/ng-template&gt;
      &lt;ng-template #controlTemplate let-name=&quot;name&quot; let-model=&quot;model&quot; let-level=&quot;level&quot;&gt;
          &lt;li class=&quot;row&quot;&gt;
              &lt;div class=&quot;strong&quot;&gt;{{name | configName}}&lt;/div&gt;
              &lt;ng-container [ngSwitch]=&quot;typeOf(obj[name])&quot;&gt;
                  &lt;input type=&quot;text&quot; *ngSwitchCase=&quot;'string'&quot; [formControlName]=&quot;name&quot;&gt;
                  &lt;input type=&quot;number&quot; *ngSwitchCase=&quot;'number'&quot; [formControlName]=&quot;name&quot;&gt;
                  &lt;input type=&quot;checkbox&quot; *ngSwitchCase=&quot;'boolean'&quot; [formControlName]=&quot;name&quot;&gt;
              &lt;/ng-container&gt;
          &lt;/li&gt;
      &lt;/ng-template&gt;
  &lt;/form&gt;
  </code></pre>
  <p>Let me break it down for you, in case the component's template isn't understood:</p>
  <p>I have <code>formItemTemplate</code> whose job is to distinguish if <code>parent.get(name)</code> (the child control) is a <code>FormGroup</code> or a <code>FormControl</code>. If the child control is a <code>FormGroup</code>, a child template of <code>groupTemplate</code> is created, otherwise <code>controlTemplate</code>.</p>
  <p>In <code>groupTemplate</code> I created a <code>ul</code> node which binds <code>[formGroup]</code> to <code>parent.get(name)</code> (the child group) and <strong>in it</strong> (hierarchically) then proceed to create a <code>formItemTemplate</code> template <em>for each control</em> of that child group, whose control names I got using a <code>keys</code> pipe which I've created, which just returns <code>Object.keys()</code> for the given value. Bascially - recursion.</p>
  <p>In <code>controlTemplate</code> I just create a new <code>li</code> and in it I create an input which binds <code>[formControlName]</code> to the <code>name</code> given in <code>[ngTemplateOutletContext]</code>.</p>
  <p>The problem is that I get the following error:</p>
  <blockquote>
  <p>Cannot find control with the name: '...'</p>
  </blockquote>
  <p>I know it happens if you bound <code>[formControlName]</code> on a control with a name that isn't found in the scope of the <code>[formGroup]</code> bound to your FormGroup. That is weird, because I even checked in DevTools and can see that <code>ng-reflect-form=&quot;[object Object]&quot;</code> attribute is applied to the <code>ul</code> of the group, which means it indeed is bound and most probably to the correct <code>FormGroup</code>.</p>
  <p>When debugging <code>isGroup()</code> with additional arguments passed from the template's context (such as <code>model</code> and <code>name</code>) I've noticed that <code>isGroup()</code> is called many times for each object, sequentially, and not once for each object, like so:</p>
  <ol>
  <li>config</li>
  <li>database</li>
  <li>uri</li>
  <li>config - again? why? why wasn't it called for <code>name</code>?</li>
  <li>database</li>
  <li>uri</li>
  <li>name</li>
  <li>config - 3rd time???</li>
  <li>database</li>
  <li>uri</li>
  <li>name</li>
  <li>server - should've been called in the 5th iteration</li>
  </ol>
  <p>And then it crashes without completing the iteration over the whole model, unless I click on some input in my component, which is <em>so weird</em>.</p>
  <p>I've seen <a href="https://stackoverflow.com/questions/45658033/angular-4-dynamic-form-with-nested-groups">this question</a> which is similar to mine but it wasn't helpful for problem. Thanks in advance!</p>