angularangular-formsangular-template

In Angular how to segregate the part of reactive form in seperate ng-template (s) inside same component?


I want to use some part of the angular reactive form will come from an ng-template. Seperating in different component will work fine with ngViewProviders. But I want to achieve it via ng-template.

        <h1>Test Reactive Form</h1>
        <form [formGroup]="form">
          <ng-container
          [ngTemplateOutlet]="formArray"
          [ngTemplateOutletContext]="{formGroup:form, arrayName:test}"
          ></ng-container>
        </form> 
        {{form.value | json}}

        <ng-template #formArray let-formGroup="formGroup" let-arrayName="arrayName"  >
          
          <ng-container [formGroup]="formGroup">
        <div formArray="arrayName">
            <ng-container *ngFor="let t of arrayName.controls;let i = index;" [formGroupName]="i">
              {{formGroup.value | json}}
            <input type="text"/>
            </ng-container>
          </div>
          </ng-container>
        </ng-template>
      `,
    })
    export class App {
      name = 'Angular';
      form;
      constructor(private fb: FormBuilder) {
        this.form = this.fb.group({
          test: this.fb.array([]),
        });

        const test = this.fb.group({
          name: 'Test name',
        });
        this.test.push(test);
        const test_1 = this.fb.group({
          name: 'test name 2',
        });
        this.test.push(test_1);
      }

      get test(): FormArray {
        return this.form.get('test') as FormArray;
      }

Getting erro like ERROR Error: Cannot find control with name: '0'

Stackbitz link: https://stackblitz.com/edit/angular-9jmcfq?file=src%2Fmain.ts


Solution

  • the attributes formGroupName, formGroup, formArray and formArrayName must have a proper HTML element defined, removed all ng-container and replaced with div.

    For defining form array you should use formArrayName, the name should match the label test that you defined inside the formGroup({...}).

    The input inside the for loop should have formControlName="name" added.

      <ng-template #formArray let-formGroup="formGroup" let-arrayName="arrayName"  >
        <div [formGroup]="formGroup">
          <div formArrayName="test">
            <div *ngFor="let t of arrayName.controls;let i = index;" [formGroupName]="i">
              <input type="text" formControlName="name"/>
            </div>
          </div>
        </div>
      </ng-template>
    

    Full Code:

    import 'zone.js';
    import { Component } from '@angular/core';
    import { bootstrapApplication } from '@angular/platform-browser';
    import {
      FormArray,
      FormBuilder,
      FormsModule,
      ReactiveFormsModule,
    } from '@angular/forms';
    import { CommonModule } from '@angular/common';
    
    @Component({
      selector: 'app-root',
      standalone: true,
      imports: [FormsModule, ReactiveFormsModule, CommonModule],
      template: `
      <h1>Test Reactive Form</h1>
      <form [formGroup]="form">
        <ng-container
        [ngTemplateOutlet]="formArray"
        [ngTemplateOutletContext]="{formGroup:form, arrayName:test}"
        ></ng-container>
      </form> 
      {{form.value | json}}
    
      <ng-template #formArray let-formGroup="formGroup" let-arrayName="arrayName"  >
        <div [formGroup]="formGroup">
          <div formArrayName="test">
            <div *ngFor="let t of arrayName.controls;let i = index;" [formGroupName]="i">
              <input type="text" formControlName="name"/>
            </div>
          </div>
        </div>
      </ng-template>
              `,
    })
    export class App {
      name = 'Angular';
      form;
      constructor(private fb: FormBuilder) {
        this.form = this.fb.group({
          test: this.fb.array([]),
        });
    
        const test = this.fb.group({
          name: 'Abhisek',
        });
        this.test.push(test);
        const test_1 = this.fb.group({
          name: 'Abhisek2',
        });
        this.test.push(test_1);
      }
    
      get test(): FormArray {
        return this.form.get('test') as FormArray;
      }
    }
    
    bootstrapApplication(App);
    

    Stackblitz Demo