Mastering Angular Reactive Forms - Harnessing the Power of FormArray with Multiple FormGroup Control Arrays

Trying to replicate my model in a reactive form has proven challenging. I've organized FormGroups to structure the data according to my model, but I'm struggling with setting up the component to properly display the values. This suggests that there may be an issue with how my template is configured as well.

Depending on whether I am editing an existing location or creating a new one,

@Input() location: ILocation;

could potentially be undefined. At the moment, my focus is solely on working with an existing location where location definitely contains a value.

// location.model.ts

name: string;
...
messaging: [
    {
        email: {
            fromName: string;
            fromAddress: string;
        };
    }
];
...
createdAt?: string;
updatedAt?: string;
deletedAt?: string;

In the template, I'm utilizing ngClass for validation feedback:

// location.commponent.html

<div formGroupName="messaging">
    <div formGroupName="email">
        ...
        <div [ngClass]="form.get(['messaging', 'email', 'fromName'])!.errors && (form.get(['messaging', 'email', 'fromName'])!.dirty || form.get(['messaging', 'email', 'fromName'])!.touched) ? 'red' : 'green'">
            <input name="fromName"/>
        </div>
        <!-- fromAddress -->
    </div>
</div>

Within the component, I pass the model using input binding and configure the form group(s) and form fields like this:

// location.component.ts

@Input() location: ILocation; 

form: FormGroup;

...

ngOnInit(): void {
    this.form = new FormGroup({name: new FormControl(this.location.name, [Validators.required]),
    messaging: new FormGroup({
    email: new FormGroup({
        fromName: new FormControl(this.location.messaging[0].email.fromName, [Validators.required]),
        fromAddress: new FormControl(this.location.messaging[0].email.fromAddress, [Validators.required]),
        }),
    }),
}

The current error being encountered is:

Cannot read properties of undefined (reading 'email')

If I log what's in the component:

console.log('messaging: ', this.location.messaging);

// email: {fromName: 'No Reply <<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="137d7c6176637f6a53766b8a41484847758860414149">[email protected]</a>>', fromAddress: '<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="e58b8a978095899ca5809d9461cac8cbcfc2cd94d6dbdbe4d4">[email protected]</a>'}

I have attempted different methods such as messaging['email'] or messaging.email messaging[0] without success in finding the correct path.

I also question if the get() method is correctly used in my template.

How can I correctly set up my form to read/present the data?

Update:

Unsurprisingly, a major issue was arising from sending back data in the wrong format.

Ultimately, here is the JSON structure I aim to create:

[{"email":{"fromName":"No Reply <<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="d5bbbaa7b0a5b9ac95b0adb99cb3bdbab5b1beb197bab6b4">[email protected]</a>>","fromAddress":"<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="254b4a574055495c65405d59527558525659f075789a767f78847893de939f9dd8">[email protected]</a>"}}]

It appears that using FormArray is necessary to send the appropriate data structure:

messaging: new FormArray([
    new FormGroup({
        email: new FormGroup({
            fromName: new FormControl(this.location.messaging[0].email.fromName, [Validators.required]),
            fromAddress: new FormControl(this.location.messaging[0].email.fromAddress, [Validators.required]),
        }),
    }),
]),

This poses issues in my template as I currently have:

form.get('messaging[0].email.fromAddress')

Resulting in:

Error: Cannot find control with path: 'messaging -> email'

I believe I need to somehow iterate through the FormArray. However, this array isn't dynamic, as I will always have email, along with only fromName and fromAddress.

Answer №1

A FormArray is necessary because the messaging field is an array.

In order to access each element in the FormArray, you need to use *ngFor and provide the index (i):

form.get(['messaging', i, 'email', 'fromName'])

The hierarchy of your template form from the parent FormGroup to the fromName FormControl should be as follows:

form (FormGroup) --> messaging (FormArray) --> i (FormGroup) --> email (FormGroup) --> fromName (FormControl)

Your HTML template code should look like this:

<div [formGroup]="form">
  <div
    formArrayName="messaging"
    *ngFor="let control of messaging.controls; let i = index"
  >
    <ng-container [formGroupName]="i">
      <div formGroupName="email">
        ...
        <div
          [ngClass]="
            form.get(['messaging', i, 'email', 'fromName'])!.errors &&
            (form.get(['messaging', i, 'email', 'fromName'])!.dirty ||
              form.get(['messaging', i, 'email', 'fromName'])!.touched)
              ? 'red'
              : 'green'
          "
        >
          <input formControlName="fromName" />
        </div>
        <div>
          <!-- fromAddress -->
          <input formControlName="fromAddress" />
        </div>
      </div>
    </ng-container>
  </div>
</div>
get messaging(): FormArray {
  return this.form.get('messaging') as FormArray;
}

Check out the demo on StackBlitz

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

Achieving text alignment with icons in Angular

I'm having trouble aligning my text with the icon next to it despite trying to adjust margins. I would really appreciate any help or suggestions on how to resolve this issue. <div class="notification" [ngClass]="{'no ...

watcher using RxJS

After recently diving into the Observable and Observer pattern, I came across various resources that describe Observable as a producer and Observer as a consumer. However, as I analyzed the code snippet below, I found myself puzzled by the role of the obse ...

What is the best way to accurately parse a Date object within a TypeScript class when the HttpClient mapping is not working correctly?

Task.ts: export class Task { name: string; dueDate: Date; } tasks.service.ts: @Injectable() export class TasksService { constructor(private http: HttpClient) { } getTasks(): Observable<Task[]> { return this.http.get<Ta ...

Property '{}' is not defined in type - Angular version 9.1.1

Currently, I am working with Angular CLI version 9.1.1 and I am attempting to update certain data without updating all of it. form: UserInfo = {adresse : {}}; UserInfo.interface export interface UserInfo { id_user: string; username: string; em ...

Encountering an issue with undefined error when performing two-way form binding with an object that implements an interface

I have defined an User interface: export interface User{ name:string; email:string: address:string; } After importing this interface on my Ionic page, I declared the following code in the class, just before the constructor: user:User; Later, in the ngO ...

Can you provide guidance on how to access my account using the code that I have?

I'm having trouble getting the login functionality to work properly with this code. When I click the login button, nothing happens - no errors are displayed either. Can you help me identify what might be causing this issue? login() { var url = &ap ...

Launching Angular 4 front-end and .Net Web Api back-end simultaneously on the same IIS website results in a 404 error when refreshing the page

My web application consists of an Angular 4 front-end client-side code that communicates with a back-end services part written in ASP.NET WebAPI. I have deployed the application on IIS v10, within the same website. However, whenever I try to refresh the pa ...

Executing several Angular applications on a shared Node server

Currently learning about the MEAN stack, I am looking to create an application that includes both admin and client sections. To accomplish this, I have developed two Angular 2 apps within my Node environment. However, I am experiencing difficulty in render ...

Implementing onClick event handling in Material UI components using Typescript

I am attempting to pass a function that returns another function to material UI's onTouchTap event: <IconButton onTouchTap={onObjectClick(object)} style={iconButtonStyle} > <img alt={customer.name} className="object-img" src={obj ...

Is it possible to navigate to a different section of a webpage while also jumping to a specific id within that section seamlessly?

I'm trying to create a navbar link that will take me directly to a specific section of a page, but I'm having trouble getting it to work. Here's what I've tried: <a href="home#id" class="nav-link text on-hover"></a> Where ...

Learn how to customize the signature of the onClick event in TypeScript

Looking at a sub-component example: import React from 'react'; interface TodoListProps { items: { id: string; text: string }[]; buttonHandler: (todoId: string) => void; } const TodoList: React.FC<TodoListProps> = (props) => { ...

A guide on validating dates in Angular Ionic5 with the help of TypeScript

I have tried multiple solutions, but none seem to work when validating the current date with the date entered by the user. The date is passed from the user into the function parameters, but how do I perform validation? How can I validate the date? isToday( ...

The Angular2 cli throws an error stating: "Cannot add a new entry to an existing one."

I have been utilizing the Angular2 Cli as my runtime environment for my Angular 2 application and I must say, I am thoroughly impressed by its architecture, top-notch development tools, and overall well-thought-out design. However, every so often, specifi ...

Utilizing Angular Firestore in Combination with Await

Upon reviewing this response, I attempted to implement async/await with a firestore call but it seems like I may be overlooking something. The aim is to fetch a collection of 'hex' documents for a hex grid using Snapshot. Initially, I had valueC ...

Tips for updating static array data with API requests in Angular

How can I replace the existing static array data with the response data from an API request? Below is a sample array of data that I would like to replace with the data obtained from the API response. How should I go about accomplishing this task? I am cu ...

Updating from React 17 to React 18 in Typescript? The Children of ReactNode type no longer supports inline conditional rendering of `void`

When using the React.ReactNode type for children, inline conditional renders can cause failures. Currently, I am utilizing SWR to fetch data which is resulting in an error message like this: Type 'false | void | Element | undefined' is not assig ...

Tips for indicating ngbDatepicker as valid in a form even without selecting a value

In my Angular2 project, I am utilizing ng-bootstrap's ngbDatepicker within a Reactive Form. The dates in this form are not required, but the problem is that ngbDatepicker always considers the form as Invalid unless a date is chosen. Is there a method ...

What are the steps to fix the configuration error caused by IIS?

I have successfully deployed my Angular application on IIS manager. After building the application with the command ng build --prod --base-href /home/, I added the data generated in the dist folder to the IIS application. Here are the files that were gen ...

Encountered a problem while trying to retrieve HTML values from an object within a ReactJS component

I have encountered an issue while working with an object that contains HTML values. When trying to access it, I am facing the following error: Element implicitly has an 'any' type because expression of type 'any' can't be used to ...

The JSX component cannot utilize 'Home' when working with React and TypeScript

I am a beginner in using React and TypeScript, and I want to retrieve data from an API and display it in a table using TypeScript and React. My project consists of two files: Home.tsx and App.tsx. The primary code that interacts with the API is located in ...