Expanding Form Arrays with Dynamically Generated Fields

One challenge I'm facing involves a form array structured as follows:

    this.myForm = this.fb.group({
      arr: this.fb.array([this.createItem()])
    })

I have the ability to dynamically append fields to the array using the following methods:

    createItem() {
    return this.fb.group({
      name: [''],
      pay: [''],
      type: []
    })
  

    addItem() {
    this.arr = this.myForm.get('arr') as FormArray;
    this.arr.push(this.createItem());}

The 'type' control operates as a dropdown with the following values:
    
  types = [
    { id: 10, name: 'A' },
    { id: 20, name: 'B' },
    { id: 30, name: 'C' },
  ];

My main concern arises when a user selects Type 'A' for a group. In such cases, I aim to include two additional fields: 'phone' and 'address'. Conversely, for selections of 'B' and 'C', I do not want these extra fields added.

For instance, if a user opts for 'A' in the first entry of the form Array, I will introduce controls for 'phone' and 'address' within the 'this.fb.group'. If the user switches to 'B' or 'C' for that specific row in the array, I will remove these two fields accordingly.

To achieve this functionality, I am keen on subscribing to the changes in the 'type' form control, enabling me to add or delete controls based on the selected type. However, I am uncertain about the exact implementation steps. Reference stackblitz can be found here.

Answer №1

To manage dynamic FormControls in Angular, you can subscribe to the valueChanges event of a FormControl and use the addControl and removeControl methods based on its value.

Here's an example code snippet to illustrate how you can achieve this:


createItem() {
  const newFormGroup = this.fb.group({
    name: [''],
    pay: [''],
    type: [],
  });
  newFormGroup.get('type').valueChanges.subscribe((value) => {  
    if (value == 10) { 
      this.addAdditionalControls(newFormGroup);
    } else {
      this.removeAdditionalControls(newFormGroup);
    }
  });
  return newFormGroup;
}

addAdditionalControls(formGroup: FormGroup) {
  formGroup.addControl('phone', new FormControl(''));
  formGroup.addControl('address', new FormControl(''));
}

removeAdditionalControls(formGroup: FormGroup) {
  if (formGroup.get('phone') && formGroup.get('address')) {
    formGroup.removeControl('phone');
    formGroup.removeControl('address');
  }
}

In your HTML file, you can conditionally display the dynamic controls like this:


<select placeholder="Type" formControlName="type">
  <option *ngFor="let type of types" [value]="type.id">
    {{ type.name }}
  </option>
</select>
<ng-container *ngIf="a.get('phone')">
  <br /><br />
  <label for="phone">Phone:</label>
  <input type="text" name="phone" formControlName="phone" />
</ng-container>
<ng-container *ngIf="a.get('address')">
  <br /><br />
  <label for="address">Address:</label>
  <input type="text" name="address" formControlName="address" />
</ng-container>

Answer №2

To optimize your FormArray, consider using an array of FormGroups with the same structure but only display inputs when the type is "A". In the HTML file, you can achieve this by adding:

*ngIf="myForm.get('arr.'+i+'.type').value!='A'

-where "i" represents the index being iterated in the formArray-. If you require validation, implement a custom validator within the group:

createItem() {
    return this.fb.group(
      {
        name: [''],
        pay: [''],
        type: [],
      },
      { validator: this.requiredIfType() }
    );
  }
  requiredIfType() {
    return (control: AbstractControl) => {
      const group = control as FormGroup;
      if (control.get('type').value != 10) return null;
      let error = null;
      if (!control.get('name').value) error = { nameRequired: true };
      if (!control.get('pay').value) error = { ...error, payRequired: true };

      return error;
    };
  }

You can then utilize the following code:

    <div class="error" *ngIf="myForm.get('arr.' + i + '.name').touched &&
                  myForm.get('arr.' + i).errors?.nameRequired">
      Name required
    </div>

    <div class="error" *ngIf="myForm.get('arr.' + i + '.pay').touched &&
                  myForm.get('arr.' + i).errors?.payRequired">
      Payment required
    </div>

For further reference, please visit this stackblitz link.

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

Having difficulties utilizing React Syntax Highlighter in Next.js 13 with Sanity version 3

Hello there, I've encountered a problem with my project while using Sanity v3 and React Syntax Highlighter. Initially, I had success displaying my code in the browser by utilizing the Refactor library after following a tutorial on the Code Input by Sa ...

Struggling to access component variables within the setTimeout function

As a newcomer to Angular(6), I am facing an issue where I am unable to access component variables within a setTimeout function. Below is the code snippet illustrating my problem. export class ConSellerDraftsolSecOneComponent implements OnInit { ...

Strategies for incorporating 'this' into a versatile method function

I'm struggling with the correct terminology for this issue, so I apologize if my title is not accurate. When attempting to utilize 'this' within a method, I am encountering an issue where it returns as 'undefined' during execution ...

Creating a specialized Angular validator that detects null values and returns the associated FormControl

I'm currently working on an Angular Reactive form group that includes a custom validator. My form includes 2 date pickers and I need to ensure that if a user selects a date from one picker, they must also select a date from the other. However, I am en ...

When utilizing a combination of generics in a Typescript mapped type, the resulting property type is not computed

In an attempt to create a versatile method that can handle a Mapped Type named QueryParamObject and partially read its properties: QueryParamObject can handle any type and returns a type where all properties are either string[] or string: export type Quer ...

Explore the titles provided by Wikipedia

Hi there, I'm fairly new to Angular and trying to work with the Wikipedia API. However, I seem to be having trouble getting 4 titles from the API. Here's an example URL for one of the titles: https://en.wikipedia.org/w/api.php?action=query&pr ...

Utilize mapping for discriminated union type narrowing instead of switch statements

My objective is to utilize an object for rendering a React component based on a data type property. I am exploring ways to avoid utilizing switch cases when narrowing a discriminated union type in TypeScript. The typical method involves using if-else or sw ...

Angular version 8.2 combined with Firebase can result in errors such as those found in the `./src/app/app.module.ngfactory.js` file towards the end of the process when attempting to build with

My first time posing a query on this platform, and certainly not my last. The issue at hand involves the ng-build --prod process failing to complete and throwing errors in my Angular 8.2.14 application. I've integrated Firebase into my project succes ...

What sets apart angular-cli from @angular/cli in the npm ecosystem?

Two angular cli packages can be found on npm: angular-cli currently at version 1.0.0-beta.28.3 @angular/cli currently at version 1.0.0-beta.31 What sets these two packages apart, and which one is recommended for a new project? The information provided ...

Guide on transforming a JSON string into an array of custom objects using the json2typescript NPM module within a TypeScript environment

I am looking to utilize the json2typescript NPM module to convert a JSON string into an array of custom objects. Below is the code I have written. export class CustomObject { constructor(private property1: string, private property2: string, private p ...

Error encountered in app.module.ts file of Angular 2 application

My friends and I are working on a big school project, creating a cool web app. Suddenly, I encountered some errors in my app.module.ts file that I haven't seen before. It's strange because they are showing up out of nowhere! The error: Error:( ...

Generating a dynamic table using Angular

My goal is to populate a table dynamically using the code below: teams.component.ts import { Component, OnInit } from '@angular/core'; import { first } from 'rxjs/operators'; import { TeamService } from 'src/app/services/team.ser ...

Use rowSelected to set the initial selected rows in an Angular Kendo UI grid

Within my Kendo UI Grid, the ability to select individual rows is made possible by clicking a checkbox that adds them to an array. However, my goal is to initially set selected rows based on whether or not the dataItem for each row exists in a specified ar ...

Is Resharper suitable for analyzing Angular code?

I'm a beginner when it comes to Resharper. Despite reading the documentation, I still have some doubts. Is it possible to use Resharper for analyzing Angular projects or TypeScript files? If so, could you please explain how to do it? ...

The integration of AngularFireFunctions with Ionic 5 Angular breaks down when attempting to create a production build using the --prod flag

Attempting to invoke a Firebase function using Angular Fire within an Ionic project. const callable = this.afFunctions.httpsCallable('getWeather'); const result = await callable(request).toPromise(); The functionality operates as expected in ...

Error in Typescript: Array containing numbers is missing index property `0`

This is the code for my class: class Point{ coordinates: [number, number, number]; constructor(coordinates: [string, string, string]) { this.coordinates = coordinates.map((coordinate) => { return Math.round(parseFloat(coordinate) *100)/ ...

Is it possible to transform a personalized typescript component into an HTMLElement within the Angular framework?

Suppose I have a customized component named 'my-component' in Angular. Is there a method to transform this component into a HTMLElement so it can be passed to a function that requires a HTMLElement as an argument? I am aware that the HTMLElemen ...

Issue: The token is required in Angular 2 RC5 to proceed with the testing

In June 2016, an article was written on testing Angular 2 applications using angular2-seed as the starting point. For a tutorial rewrite using Angular CLI (master branch) with Angular 2 RC5, a strange error is encountered during testing: Error: Token mus ...

Nesting objects within arrays using Typescript Generics

Hello, I am currently attempting to assign the correct type to an object with nested values. Here is a link to the code in the sandbox: https://codesandbox.io/s/0tftsf interface Product { name: string, id: number productList?:ProductItem[] } interf ...

Tips for ensuring an angular mat panel always remains expanded:

Is it possible to keep an angular mat panel always expanded without closing when the user clicks on the header? Thanks in advance! ...