Search code examples
angularangular-componentsangular-forms

How to pass a form as an Angular component input?


In an Angular 16 app I have a template driven form that will directly contain a text field and my submit button. It also contains a component that will contain some common reusable form fields, so I pass my form reference as an input to it.

I am having a problem where the fields inside of the component are not added to my form. How do I associate an input with a form from within the component? See my demo link below.

StackBlitz Demo here

The problem is that the fields inside the component are not added to the form, and thus when they are invalid they do not affect the parent form.

Here's the code directly in my question, but the above link will be a better example

The host component with the form

<form #myForm="ngForm" (ngSubmit)="submit(myForm)">
  <label for="fieldOne">One (directly in form)</label>
  <input
    type="text"
    id="fieldOne"
    name="fieldOne"
    [ngClass]="{
      'is-invalid': fieldOne.invalid && (fieldOne.touched || myForm.submitted)
    }"
    required
    #fieldOne="ngModel"
    [(ngModel)]="fieldOneVal"
  />

  <br /><br />

  <app-form-editor [form]="myForm"/>

  <br />
  <button type="submit">Submit</button>
  <strong *ngIf="submitSuccess" class="text-success">Sucessfully submitted!</strong>

  <br />

  <pre>myForm.valid: {{myForm.valid}}</pre>
  <pre>myForm.value: {{myForm.value | json}}</pre>
</form>

The <app-form-editor>

import { Component, Input, OnInit } from '@angular/core';
import { NgForm } from '@angular/forms';

@Component({
  selector: 'app-form-editor',
  templateUrl: './form-editor.component.html',
})
export class FormEditorComponent implements OnInit {
  @Input() form!: NgForm;

  fieldTwoVal = '';

  ngOnInit(): void {}
}
<label for="fieldTwo" class="control-label">Two (Inside Component)</label>
<input
  name="fieldTwo"
  id="fieldTwo"
  [ngClass]="{
    'is-invalid': fieldTwo.invalid && (fieldTwo.touched || form.submitted)
  }"
  required
  [(ngModel)]="fieldTwoVal"
  #fieldTwo="ngModel"
/>

<br />
<pre>component form.valid: {{ form.valid }}</pre>
<pre>component form.value: {{ form.value | json }}</pre>

Solution

  • Just add a viewProvider in your app-form-editor.

    @Component({
      selector: 'app-form-editor',
      templateUrl: './form-editor.component.html',
      viewProviders:[{ provide: ControlContainer, useExisting: NgForm }]
    })
    

    You can think like in your child component you need add a "form ngForm" but You choose not add else use the existing NOTE: If we are working with reactiveForms the viewProvider to add is the viewProviders:[{ provide: ControlContainer, useExisting: FormGroupDirective }]