Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

How to dynamically additional formControls via template outlet?

So, I have the issue that I have a base component that has a form. But I would like to be able to add another field to it. Why? Because I have four pages, with a very similar structure, that differ only in one field at most. I’ve got a stackblitz that should get the idea across as well. The rendering part is working nice and well.

But to also give a general idea:

@Component({
  selector: 'app-base-form',
  standalone: true,
  imports: [ReactiveFormsModule, CommonModule],
  template: `<form
  style="display: flex; flex-direction: column; gap: 1rem"
  [formGroup]="form"
>
  <input formControlName="search" type="text" placeholder="Search something" />

  <ng-template
    [ngTemplateOutletContext]="{ $implicit: form }"
    [ngTemplateOutlet]="seachOptionalField()!"
  ></ng-template>
</form>`,
})
export class BaseFormComponent {
  seachOptionalField = input<TemplateRef<any>>();
  fb = inject(UntypedFormBuilder);
  form = this.fb.group({
    search: this.fb.control(['']),
  });
}

The idea was initially to pass the form via the outlet context, but programmatically adding a control didn’t really impact the original form. Also, there was the question on how to apply the rendered (to be used) form control.

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

@Component({
  selector: 'app-root',
  standalone: true,
  imports: [CommonModule, BaseFormComponent],
  template: `
    <app-base-form [seachOptionalField]="searchOptionalField"></app-base-form>
    <ng-template #searchOptionalField let-form>
    <input type="text" placeholder="We just added this" />
    {{form.value | json}}
    </ng-template>
  `,
})
export class App {}

>Solution :

You need to provide a corresponding formControlName on the new controls, also wrap them on a form element inside the template.

You can also add an extra parameter, which specifies the new form controls configuration on a object which we can destructure into our form group when initializing.

Working example below with stackblitz!

Full Code:

main.ts

import { Component, inject } from '@angular/core';
import { bootstrapApplication } from '@angular/platform-browser';
import { CommonModule } from '@angular/common';
import 'zone.js';
import { BaseFormComponent } from './app/base-form/base-form.component';
import { ReactiveFormsModule, UntypedFormBuilder } from '@angular/forms';

@Component({
  selector: 'app-root',
  standalone: true,
  imports: [CommonModule, BaseFormComponent, ReactiveFormsModule],
  template: `
    <app-base-form [seachOptionalField]="searchOptionalField" [extraFormParams]="extraFormParams"></app-base-form>
    <ng-template #searchOptionalField let-form>
      <form [formGroup]="form">
        <input type="text" placeholder="We just added this" formControlName="search2"/>
    </form>
    {{form.value | json}}
    </ng-template>
  `,
})
export class App {
  fb = inject(UntypedFormBuilder);
  extraFormParams = {
    search2: this.fb.control(['']),
  };
}

bootstrapApplication(App);

base-form.ts

import { CommonModule } from '@angular/common';
import { Component, TemplateRef, inject, input } from '@angular/core';
import {
  FormGroup,
  ReactiveFormsModule,
  UntypedFormBuilder,
} from '@angular/forms';

@Component({
  selector: 'app-base-form',
  standalone: true,
  imports: [ReactiveFormsModule, CommonModule],
  template: `<form
  style="display: flex; flex-direction: column; gap: 1rem"
  [formGroup]="form"
>
  <input formControlName="search" type="text" placeholder="Search something" />

  <ng-template
    [ngTemplateOutletContext]="{ $implicit: form }"
    [ngTemplateOutlet]="seachOptionalField()!"
  ></ng-template>
</form>`,
})
export class BaseFormComponent {
  seachOptionalField = input<TemplateRef<any>>();
  extraFormParams = input<Object>({});
  fb = inject(UntypedFormBuilder);
  form!: FormGroup;

  ngOnInit() {
    this.form = this.fb.group({
      search: this.fb.control(['']),
      ...this.extraFormParams(),
    });
  }
}

Stackblitz Demo

Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading