struggling to implement dynamic reactive forms with Angular

Currently, I am experimenting with building a dynamic reactive form using Angular. While I have successfully implemented the looping functionality, I am facing some challenges in displaying it the way I want.

<form [formGroup]="registerForm" (ngSubmit)="onSubmit()">
  <div *ngFor="let dat of formData; let index = index;">
     <label for="{{dat.name}}">{{dat.name }}</label>
     <input type="text" id="{{dat.id}}" [formControlName]="dat.name" />  
    </div>


</form>
<button type="button"><button>

.ts

 registerForm: FormGroup;
  submitted = false;

  formData = [{ id: 'firstName', name: 'firstName' },
  { id: 'lastName', name: 'lastName' },
  { id: 'address', name: 'address' },
  { id: 'emailid', name: 'emailid' }
  ]


  constructor(private formBuilder: FormBuilder) {
    this.registerForm = this.formBuilder.group({
      formData: []
    });
   }

   onSubmit(){
    console.log(this.registerForm.value); 
   }

Moreover, I am looking to add validation to the form as well.

You can check out my code on StackBlitz by following this URL:

https://stackblitz.com/edit/angular-awguej

Answer №1

One way to achieve this is by using the this.formBuilder.array method in Angular.

Check out this Stackblitz example for reference.

component.html

<form [formGroup]="registerForm" (ngSubmit)="onSubmit()">
    <div formArrayName="formData">
        <div *ngFor="let dat of registerForm.get('formData').controls;let index = index;" [formGroupName]="index">
            <label>{{dat.get('name').value }}</label>
     <input type="text" formControlName="name" />  
    </div>
<button type="submit">Submit</button>
  </div>
</form>
<button type="button" (click)="addNew()">Add</button>

component.ts

registerForm: FormGroup;
  submitted = false;

  constructor(private formBuilder: FormBuilder) {
    this.registerForm = this.formBuilder.group({
      formData: this.formBuilder.array([this.createNewGroup()])
    });
   }

   createNewGroup() {
    return this.formBuilder.group({
      id: ['id'], 
      name: ['name'] 
    })
  }

  addNew(){
    let formArr=this.registerForm.get('formData') as FormArray;
    formArr.push(this.createNewGroup());
  }

   onSubmit(){
    console.log(this.registerForm.value); 
   }

Answer №2

To ensure proper control over an input field, it is essential to include an ID for control purposes, a name for labeling, and a value for setting the input value. This approach will prove beneficial when implementing a more dynamic solution.

Within your component, you can follow this structure:

formData = [{ id: 'firstName', name: 'firstName', value: '' },
  { id: 'lastName', name: 'lastName', value: '' },
  { id: 'address', name: 'address', value: '' },
  { id: 'emailid', name: 'emailid', value: '' }
  ]

constructor(private formBuilder: FormBuilder) {
    let formGroups: FormGroup[] = []; 
    for(let i = 0; i < this.formData.length; i++){
      formGroups.push(this.formBuilder.group({
          id: this.formData[i].id,
          name: this.formData[i].name,
          value: this.formData[i].value,
      }));
    }
    this.registerForm = this.formBuilder.group({
        array: this.formBuilder.array(formGroups)
    })
   }

   onSubmit(){
    console.log(this.registerForm.value); 
   }

In your HTML template, include the following:

<form [formGroup]="registerForm" (ngSubmit)="onSubmit()">
  <div formArrayName="array">
    <div *ngFor="let dat of registerForm.get('array').controls;let i = index;" [formGroupName]="i">
        <label>{{dat.get('name').value}} </label>
        <input type="string" id={{dat.value.id}} #input formControlName="value">
    </div>
  </div>
</form>

<button><button>

Answer №3

You have the option to include controls in this manner.

component.ts

import { Component, OnInit } from '@angular/core';
import { FormBuilder, FormGroup, FormArray, FormControl, ValidatorFn } from '@angular/forms';

@Component({
  selector: 'my-app',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit {

  registerForm: FormGroup;
  submitted = false;

  formData = [{ id: 'firstName', name: 'firstName' },
  { id: 'lastName', name: 'lastName' },
  { id: 'address', name: 'address' },
  { id: 'emailid', name: 'emailid' }
  ]


  constructor(private formBuilder: FormBuilder) {
    this.registerForm = this.formBuilder.group({
      list: this.formBuilder.array([])
    });
  }

  ngOnInit() {
    this.registerForm = this.formBuilder.group({
      list: this.formBuilder.array(
        this.formData.map(x => this.formBuilder.group({
          name: [x['name']],
          id: [x['id']]
        }))
      )
    })
  }

  onSubmit() {
    console.log(this.registerForm.value);
  }
}

Component.html

<form [formGroup]="registerForm" (ngSubmit)="onSubmit()">
  <span formArrayName="list">
  <div *ngFor="let dat of registerForm.get('list').controls;let index = index;" formGroupName="{{index}}">
     <label for="{{dat.name}}">{{dat.name }}</label>
     <input type="text" id="{{dat.get('id').value}}" formControlName="name" />  
    </div>
  </span>
<button type="submit">button</button>
</form>

Answer №4

To add to Krishna's response, if you're looking to generate a formArray with data input, you can utilize two specific functions.

// Function to construct a formGroup with provided data or empty if there is none
createNewGroup(data): FormGroup {
    return this.formBuilder.group({
        id: [data ? data.id : null], 
        name: [data ? data.name : null] 
    })
}

// Function for creating the actual form

createForm(formData): FormGroup {
    // Generate an array of FormGroups,
    // If there is data, create a new group for each item in the data array
    // Otherwise, create an array with one element (null)
    let group = formData ? formData.map(d => this.createNewGroup(d)) 
                        : [this.createNewGroup(null)]

    return this.formBuilder.group({
         formData: this.formBuilder.array(group)
       });
}

Therefore, you have the flexibility to:

this.registerForm = createForm(this.formData) // Generate a form with existing data
this.registerForm = createForm(null) // Generate an empty form

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

Angular4 - Div with ngIf doesn't respond to click event

I'm attempting to add a click event to a specific div. Within this div, there is another div that is dynamically generated based on a Boolean condition that I receive as input. Unfortunately, in this case, the click event is only functioning when clic ...

The REST API seems to be functioning correctly when accessed through Postman, but there are issues when attempting to call

When I include @PreAuthorize("hasRole('ROLE_SUPER_ADMIN')") in my controller and make a call from Angular, it doesn't work. However, it works fine when called from Postman. @GetMapping("/getAllOrgniz") @PreAuthorize("hasRole('ROLE_SUPE ...

Updating Angular Material theme variables during the build processIs this okay?

How can I easily customize the primary color of my angular 6 application to be different for development and production builds? Is there a simple solution to automatically change the primary color based on the build? ...

Generate several invoices with just a single click using TypeScript

I'm interested in efficiently printing multiple custom HTML invoices with just one click, similar to this example: Although I attempted to achieve this functionality using the following method, it appears to be incorrect as it prompts the print dialo ...

Troubleshooting the malfunction of the Angular 2 Tour of Heroes project following the separation of the app

Recently, I encountered a challenge while following a tutorial on learning Angular 2. Everything was going smoothly until I reached the point where I had to divide appcomponent into heroescomponent & appcomponent. Is there anyone else who has faced th ...

Applying ngClass to a row in an Angular material table

Is there a way I can utilize the select-option in an Angular select element to alter the css-class of a specific row within an Angular Material table? I have successfully implemented my selection functionality, where I am able to mark a planet as "selecte ...

Tips for displaying HTML content dynamically in React using TypeScript after setting a stateVariable

To render an HTML block after successfully setting a state variable, I have defined my state variables and functions below. The code snippet is as follows: const formService = new FormService(); const [appointmentDate, setAppointmentDate] = us ...

The specified attribute "matListItemTitle" is invalid in this context

Having trouble incorporating a list from Angular Material into an Angular project. WebStorm is showing the error mentioned in the title above. I came across the problem here, but in this scenario, I am unable to use matListItemTitle as a dynamic parameter ...

What's the best way to insert values into data binding within a Typescript/ Angular mat Table?

Objective: Create a dynamic table using JSON data <mat-table class="mat-elevation-z8" *ngIf="carrierRates" [dataSource]="carrierRates"> <ng-container *ngFor="let columnName of columnsList" matColumn ...

Determine the subtotal for a particular item using AngularJS

Is there a way to apply the sub total amount to a specific item only? Currently, in my stackblitz example, when I add a new item and change the quantity of that last item, all recently added items also get updated. I want to modify the "changeSubtotal()" ...

Angular encountering issues with loading external JavaScript files due to the error: ENOENT - indicating that the specified file or directory does

I am attempting to incorporate a bootstrap template into Angular. The template requires some external JavaScript and CSS files that need to be linked. I have placed these files in the assets folder and referenced them in the styles and scripts arrays of an ...

Is there a way to modify a single object within an array?

Here is the HTML representation of my data: page.html <ul id="elements"> <li *ngFor="let elem of fetchdata" (click)="log(elem)"> {{elem.title}} {{elem.description}} </li> </ul> page.ts This ...

Utilizing Angular to import an SVG file from a backend and incorporate its content as a template

I am looking for a solution to load an SVG date from my Spring Boot backend and utilize it as an Angular template. Currently, the request is structured like this: getSVG (): Observable <any> { return this.http.get(`${environment.apiUrl}/path ...

Guide to specifying the indexer type of a function argument as T[K] being of type X in order for t[k]=x to be a permissible expression

Currently, I am attempting to create a function that can alter a boolean property within an object based on the provided object and property name. While I have found some helpful information here, the function body is still producing errors. Is there a way ...

How can we utilize caching for an HTTP GET request in Angular 9 only when the navigator is offline?

My Angular 9 application utilizes http.get calls to communicate with an API, and I have integrated angular-pwa for offline capabilities. One challenge I am facing is ensuring that when the browser is online, two specific requests do not use cached respons ...

How can I perform email validation using Angular 6?

I am working on an Angular6 form that includes a field for email input. Currently, the email field has proper validation which displays an error message when an invalid email is entered. However, even if the error message is shown, the form is still saved ...

Typescript with Angular: Despite having 7 values in the map, Map.get is returning undefined

Why does Map.get always return undefined when using a number from a form element (extra1) in this code snippet? extraById = new Map<number,Extra>(); @Input() extra1: number = -1; formChanged(carConfigurationFormChanged : any) { const index ...

Primeng - Displaying names in editable datatable with multiSelect feature

Lately, I have been exploring primeng and I am interested in creating an editable table that includes a multi-select column. After some experimentation, I managed to achieve this result. However, my issue is that I want the winners field (which contains a ...

The process of upgrading all dependencies to a particular version

I need guidance on upgrading a project from Angular 6 to version 7. The challenge lies in updating numerous dependencies as well. Most tutorials available only cover upgrading to the latest version (v8), rather than a specific one. Can anyone provide ins ...

What is the best way to rid ourselves of unwanted values?

In the laravel-vue-boilerplate package, there is a User CRUD feature. I duplicated this functionality to create an Item CRUD by making some changes and adjustments. Everything is working fine except for one issue: after editing an item, when trying to add ...