Search code examples
angularangular-forms

Angular template driven form: input "component" not binding to form


I'm trying to generate form fields dynamically based on some JSON object(config). This said config object contains all the attribute values required for constructing form fields(ex: label, type, placeholder etc).

Setup

In my parent component: I have a form, under which I have an ngFor, iterating over the config object, trying to generate the fields.

<form #dynamicForm="ngForm">
  <div *ngFor="let field of formFields">    <!-- formFields is the config object -->
    <app-form-field [props]="field" [formData]="formData"></app-form-field>
  </div>
  <button [disabled]="!dynamicForm.form.valid">Submit</button>
</form>

To avoid clogging the parent component, I have created a generic <form-field> component, which takes all the field attributes(as props) and a reference object(to bind the field's value).

<div *ngIf="props.type === 'text'">         <!-- One div for each input type -->
  <label [for]="props.id">{{ props.label }}</label>
  <input
    #field="ngModel"
    [id]="props.id"
    [type]="props.type"
    [attr.name]="props.name"
    [placeholder]="props.placeholder"
    [(ngModel)]="formModel[props['apiDataKey']]"
    [required]="props.isRequired"
    [disabled]="props.isDisabled"
  >
</div>

<!-- Other input types here -->

Question

The form tag contains a local reference #dynamicForm="ngForm" -> which never seems to update.

While the individual field validations work perfectly fine, the main form(dynamicForm.value) seems to:

  • contain no data
  • as a result of which, the form always shows as valid (dynamicForm.form.valid is always true)

I've created a demo on stackblitz here: https://stackblitz.com/edit/angular-ivy-nvm4hd?file=src/app/app.component.html

PS: I'm pretty new to angular2+, and can't seem to figure out what it is that I'm missing.

Thanks a bunch in advance 🙂


Solution

  • In the Child Component viewProviders array, we can provide ControlContainer to use existing NgForm instance:

    @Component({
     ...,
     viewProviders: [ { provide: ControlContainer, useExisting: NgForm } ]
    })
    export class FormFieldComponent implements OnInit {
     ...
    }