Creating a Form in Angular That Dynamically Adds Form Controls

In order to achieve my objective of incorporating a search form with multiple filters on any given page, I aim to implement the following structure on my HomePageComponent:

<app-search-form>
  <app-searchbox></app-searchbox>
</app-search-form>

Currently, my SearchFormComponent implementation is as follows:

<form [formGroup]="form" (ngSubmit)="search()">
  <ng-content></ng-content>
  <br />
  <button type="submit">Search</button>
</form>

SearchboxComponent details:

<label for="searchbox">Search:</label>
<input id="searchbox" formControlName="searchbox" type="text" />

For further enhancements, components like SearchCategoryFilterComponent and SearchLocationFilterComponent can be added within the <app-search-form> as needed.

However, I encounter an error with the current setup:

NullInjectorError: NullInjectorError: No provider for ControlContainer!
This issue arises because when
<app-searchbox></app-searchbox>
is inserted in HomePageComponent, it perceives HomePageComponent rather than the SearchFormComponent as the parent, resulting in the inability to inject the ControlContainer. Moving
<app-searchbox></app-searchbox>
within the <form> section of SearchFormComponent resolves the problem and ensures proper functioning of the form submission.

How can I configure this setup to allow HomePageComponent to define the content of the form? This approach not only facilitates the addition of various filters on each page but also enables customization of filter styles for individual pages.

Answer №1

In this scenario, the issue arises because content projection initializes the projected content on the parent level where there is no form to contain the ControlContainer, resulting in an error.


To resolve this issue, consider rendering dynamic components using the *ngComponentOutlet directive within a ng-container.

<form [formGroup]="form" (ngSubmit)="search()">
  <ng-container *ngComponentOutlet="formComponent"></ng-container>
  <br />
  <button type="submit">Search</button>
</form>

Pass the component you want to initialize as an @Input.

@Component({
  selector: 'app-root',
  standalone: true,
  imports: [SearchFormComponent, SearchboxComponent],
  template: `
    <app-search-form [formComponent]="component">
    </app-search-form>
  `,
})
export class App {
  name = 'Angular';
  component = SearchboxComponent;
}

Full Code:

import { Component, inject, OnInit, Input } from '@angular/core';
import { bootstrapApplication } from '@angular/platform-browser';
import {
  ReactiveFormsModule,
  ControlContainer,
  FormGroup,
  FormControl,
  FormBuilder,
} from '@angular/forms';
import { CommonModule } from '@angular/common';

@Component({
  selector: 'app-searchbox',
  standalone: true,
  imports: [ReactiveFormsModule],
  template: `
    <label for="searchbox">Search:</label>
    <input id="searchbox" formControlName="searchbox" type="text" />
  `,
  viewProviders: [
    {
      provide: ControlContainer,
      useFactory: () => inject(ControlContainer, { skipSelf: true }),
    },
  ],
})
export class SearchboxComponent implements OnInit {
  formGroup!: FormGroup;

  constructor(private controlContainer: ControlContainer) {}

  ngOnInit() {
    this.formGroup = this.controlContainer.control as FormGroup;
    this.formGroup.addControl('searchbox', new FormControl('', null));
  }
}

@Component({
  selector: 'app-search-form',
  standalone: true,
  imports: [ReactiveFormsModule, SearchboxComponent, CommonModule],
  template: `
  <form [formGroup]="form" (ngSubmit)="search()">
    <ng-container *ngComponentOutlet="formComponent"></ng-container>
    <br />
    <button type="submit">Search</button>
  </form>
  `,
})
export class SearchFormComponent implements OnInit {
  @Input() formComponent: any;
  form!: FormGroup;

  constructor(private formBuilder: FormBuilder) {}

  ngOnInit() {
    this.form = this.formBuilder.group({});
  }

  search() {
    console.log('Searching...');
    console.log(this.form.value);
  }
}

@Component({
  selector: 'app-root',
  standalone: true,
  imports: [SearchFormComponent, SearchboxComponent],
  template: `
    <app-search-form [formComponent]="component">
    </app-search-form>
  `,
})
export class App {
  name = 'Angular';
  component = SearchboxComponent;
}

bootstrapApplication(App);

Check out the Stackblitz Demo

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

mat-sidenav is exempt from the fxFlex rules

Having trouble centering the content within . I've attempted various rules but nothing seems to be working. Interestingly, when I apply fxFlex rules to , everything falls into place perfectly. I've gone through trying to set rules for each elemen ...

Using Angular with Web API to transmit FormFile data from client to API

I am facing a challenge with creating an object (referred to as a "Ticket") along with 0-n children (known as "Attachments") in my Angular application and sending this information to my dotnet core Web API. However, this is more of a logical inquiry that I ...

How can you eliminate the first elements of two or more arrays of objects until all of their first elements match based on a specific field?

My Typescript code includes a Map object called `stat_map` defined as const stat_map: Map<string, IMonthlyStat[]> = new Map(); The interface IMonthlyStat is structured as shown below (Note that there are more fields in reality) export interface IMon ...

What is the best way to utilize ngStyle in combination with Interpolation?

Within my application, I am faced with a challenge involving two slide bars that generate values ranging from 1 to 100. Based on these generated values, I aim to adjust the margin of a div element in accordance with the percentage output. Despite conductin ...

Creating a custom URL in a React TypeScript project using Class components

I have been researching stack overflow topics, but they all seem to use function components. I am curious about how to create a custom URL in TypeScript with Class Components, for example http://localhost:3000/user/:userUid. I attempted the following: The ...

The sole coding platform that fails to acknowledge the "ng" command is Visual Studio Code

Many users have encountered issues where the computer does not recognize 'ng,' but my problem differs from that. Interestingly, every software with a command shell recognizes 'ng' except for VS Code. IntelliJ? Works fine. Git bash? N ...

Error in Typescript TS2322: Observable Type 'boolean | {}' - Using Angular 5 and Typescript 2.4.2

After upgrading from version 4 to 5, I'm puzzled by the plethora of TypeScript TS2322 errors I'm encountering. The migration involved setting up a new Angular project with the Angular CLI. Angular CLI: 1.5.5 Node: 8.9.1 OS: darwin x64 Angular: 5 ...

How to disable the onChange event in PrimeNG p-dropdown?

I'm currently utilizing PrimeNG's dropdown component. Each option in the list includes an icon that, when clicked, should trigger a specific method. Additionally, I need to execute another method when the onChange event of the dropdown is trigger ...

Issue encountered while creating Next.js 13.4 application: The type 'ResolvingMetadata | undefined' does not meet the requirement of 'ResolvingMetadata'

I'm currently working on dynamically generating metadata for my Next.js 13.4 application using this code snippet: export async function generateMetadata( { params, searchParams }: Props, ) { try { // extract the route parameters ...

Data from HTML not being transferred by Angular Forms

I am facing an issue with transferring input data from HTML's <select> element to Angular Forms. Let's take a look at my code first. File Name: home-page.component.html <form [formGroup]="rForm" (ngSubmit)="addPaste(rForm.value)"> ...

Use Ramda to convert an array of objects into nested objects

As a beginner, please forgive me for asking what may be considered a naive question. I currently have an array of objects const arr = [{id: 1, name: 'Pete'}, {id: 5, name: 'John'}, {id: 3, name: 'Peter'}] and I am looking to ...

Discovering alterations in a component's attribute -Ang8

In my component, there are buttons to set the properties as follows: dataType:String = null fechaI : Date = null fechaF :Date = null checkedList =[] I need to trigger an HTTP request when all properties have values and redo the request if any of them ...

Using TypeScript generics to constrain to either a number or a string

I am working on coding a react input component that accepts a defaultValue parameter of type string | number. This component has a state type matching the type of the received defaultValue; This is my code: type TypeName<T> = T extends number ? "nu ...

Upon receiving data from the Api, the data cannot be assigned to the appropriate datatype within the Angular Object

I am encountering an issue with the normal input fields on my page: https://i.stack.imgur.com/qigTr.png Whenever I click on the "+" button, it triggers an action which in turn calls a service class with simple JSON data. My intention is to set selectionC ...

add the string to the chat messages array in the observable

Currently, I am in the process of developing a chat application and my goal is to showcase the user's messages in the chatroom, referred to as the feed in this project. I have already implemented a function called getMessages() that displays all exist ...

Is there a way to receive additional assistance?

Feeling a bit embarrassed, I must confess that I have just spent a considerable amount of time resolving the frustrating issue: **Syntax Error: Unexpected token <** The reason for this error was that I had overlooked adding the following line to my in ...

Tips for importing a module such as 'MyPersonalLibrary/data'

Currently, I am developing a project with two packages using Typescript and React-Native: The first package, PackageA (which is considered the leaf package), includes a REST client and mocks: MyOwnLibrary - src - tests - mocks - restClientMoc ...

Angular/NX - Element 'Component' is unrecognized, despite being imported within the module

Encountering the following issue: 'apocaloot-building' is showing as an unknown element: If 'apocaloot-building' is supposed to be an Angular component, please ensure that it is included in this module. The component in question is ...

Exploring Typescript Logging with Bunyan and Logentries

Looking to implement remote Logging using logentries.com for my ionic app. Snippet from my package.json: "dependencies": { "bunyan": "^1.8.5", "bunyan-logentries": "^1.2.0", }, "devDependencies": { "@types/bunyan": "0.0.35", "@typ ...

Can a component's route be dynamically changed based on its state?

Within an Angular 12 application, there exists a component known as SignUpComponent: export class SignUpComponent implements OnInit { form: FormGroup; result: boolean; constructor(private formBuilder: FormBuilder, private userService: UserService) ...