Insert information into a 3-tiered nested Angular FormArray within interconnected dropdown fields

After trying to retrieve data from an API call to populate a select form field, I encountered difficulties setting the value correctly using a FormArray.

This led me to creating a FormArray with 3 nested levels in Angular, taking reference from this example - https://stackblitz.com/edit/nested-formarrays-3-levels.

The complexity arose when I added level W within X, including dependent select form fields for state and city. Additionally, I added select form fields at levels Y and Z, both of which are also dependent on each other.

Here is my attempt - https://stackblitz.com/edit/hashtaagblog2-1-nhxmhz

The issues I am facing include:

  • Inability to populate the state and city data into select options from JSON data.
  • Dependency between select fields at levels Y and Z not populating data from JSON source.

Answer №1

When dealing with intricate FormsArrays, it is advantageous

1.-To utilize functions that retrieve the formArrays. Note that "XsArray" can act as a getter, whereas others must function as regular functions since they require an index. So

get XsArray() {
    return this.form.get("Xs") as FormArray;
  }
  getYsArray(index) {
    return this.XsArray.at(index).get("Ys") as FormArray;
  }
  getWsArray(index) {
    return this.XsArray.at(index).get("Ws") as FormArray;
  }
  getZsArray(index, indexz) {
    return this.getYsArray(index).at(indexz).get("Zs") as FormArray;

You can observe how the function XsArray can be used within the getWsArray function, for instance.

2.-Another aspect is to create functions that return formGroups. These functions receive either an object or null. When null is provided, we add a default object. Here they are:

setXs(el:any=null)
 {
   el=el||{X:null}
   return this.fb.group({
     X:[el.X,Validators.required],
     Ys:el.Ys?this.fb.array(el.Ys.map(x=>this.setYs(x))):this.fb.array([]),
     Ws:el.Ws?this.fb.array(el.Ws.map(x=>this.setWs(x))):this.fb.array([])
   })
 }
 setYs(el:any=null)
 {
  el=el || {product:null}
  return this.fb.group({
    product:[el.product, [Validators.required]],
    Zs:el.Zs?this.fb.array(el.Zs.map(x=>this.setZs(x))):this.fb.array([])
  })
 }
 setWs(el:any=null){
   el=el || {state:null,city:null}
   return this.fb.group({
      state: [el.state, [Validators.required]],
      city: [el.city, [Validators.required]]
   })
 }
 setZs(el:any=null)
 {
   el=el || {Z:null}
   return this.fb.group({
     Z:[el.Z, [Validators.required, Validators.pattern("[0-9]{3}])]
   })
 

Hey! pause for a moment, what does this peculiar expression mean:

this.fb.array(el.Zs.map(x=>this.setZs(x)))
. Essentially, el.Zs represents an array, where each element of the array is transformed into a FormGroup (recall that this.setZs(x) returns a formGroup), and then a formArray is created using these formGroups. Although the concept may seem complex, it can be broken down into several steps.

//this.fb.array(el.Zs.map(x=>this.setZs(x)))
//is equivalent to
  let arr=this.fb.array([])
  el.Zs.forEach(x=>{
     arr.push(this.setZs(x))
  })

Now, everything seems more straightforward. Notice how easy it becomes to add new elements using these functions:

  addX() {
    this.XsArray.push(this.setXs());
  }

  addY(ix) {
    this.getYsArray(ix).push(this.setYs());
  }

  addZ(ix, iy) {
    this.getZsArray(ix,iy).push(this.setZs());
  }

  addW(ix) {
    this.getYsArray(ix).push(this.setWs())
  }

And removing them is just as simple, e.g.

  removeW(ix, iw) {
    this.getWsArray(ix).removeAt(iw)
  }

Finally, in the ngOnInit method, we apply the seedData like so:

this.form = this.fb.group({
  Xs: this.fb.array(this.seedData.Xs.map(x=>this.setXs(x)))
});

When displaying in .html, we also employ the aforementioned functions by iterating over the formArray.controls

<form [formGroup]="form">
  <div formArrayName="Xs">
    <div *ngFor="let X of XsArray.controls; let ix=index">
      <div [formGroupName]="ix" class="Xs">
        <input type="text" formControlName="X">
        ...
        <div formArrayName="Ys">
          <div *ngFor="let Y of getYsArray(ix).controls; let iy=index">
            <div [formGroupName]="iy" class="Ys">
              ...
              <div formArrayName="Zs">
                <div *ngFor="let Z of getZsArray(ix,iy).controls; let iz=index">
                  <div [formGroupName]="iz" class="Zs">
                   ....
                  </div>
                </div>
                ...
              </div>
            </div>
          </div>
          ...
        </div>
        <div formArrayName="Ws">
          <div *ngFor="let W of getWsArray(ix).controls; let iw=index">
            <div [formGroupName]="iw" class="Ws">
                ....
            </div>
          </div>
          <input type="button" (click)="addW(ix)" value="Add W">
        </div>
      </div>
    </div>
    ...
  </div>
  <form>

The stackblitz can be found here

NOTE: A FormArray can consist of a FormArray of FormGroup or a FormArray of FormControls. If it only has a single property (e.g. the FormArray "Zs"), typically a FormArray of FormControls should be utilized.

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

Ionic is encountering a problem with module build, specifically an error related to the absence of a file or directory

Using Ionic has led to encountering the following error message: Runtime Error Uncaught (in promise): Error: Module build failed: Error: ENOENT: no such file or directory, open '/Users/richardmarais/Development/ionic/theWhoZoo/src/pages/model/r ...

Discovering the generic type from an optional parameter within a constructor

Looking to implement an optional parameter within a constructor, where the type is automatically determined based on the property's type. However, when no argument is provided, TypeScript defaults to the type "unknown" rather than inferring it as "und ...

Is it possible to build a Node.js (Express) application using a combination of JavaScript and TypeScript?

Currently, I have a Node.js project (Express REST API) that is written in JavaScript. My team and I have made the decision to gradually rewrite the entire project into TypeScript. Is it possible to do this incrementally, part by part, while still being ab ...

Changing the background color of a selection in Angular can be customized by modifying

Is there a way to customize the background color of a select element in Angular? I would like to change the default grey background to white. How can this be achieved? Below is the code for my select element: <select id="areaOfExpertiseFilter" ...

Are you delving into the realm of reduce functions in order to grasp the intric

Currently following this particular tutorial where they utilize the reduce method to transform an Array<Student> into a { [key: string]: Array<string | number> }. The tutorial includes this expression that caught my attention. It's quite n ...

Troubleshooting Issue with Mongoose Virtual Field Population

I am currently facing an issue with my database due to using an outdated backend wrapper (Parse Server). The problem arises when dealing with two collections, Users and Stores, where each user is associated with just one address. const user = { id: &q ...

Is there something I'm missing? The action buttons cannot be displayed on a preview of the event

Currently in the process of developing an angular application featuring a calendar component to showcase events, I opted to utilize angular-calendar for the visual representation. While exploring the month view functionality, I encountered an issue where t ...

Change the ddmmyy date string to dd/mm/yyyy format using TypeScript

When I use the date picker onchange method, the date is passed as a string like ddmmyyyy (20102020) to dd/mm/yyyy. I am unsure of how to convert the date format from ddmmyyyy to dd/mm/yyyy. In the CustomDateParserFormatter, the string needs to be converted ...

What steps do I need to take to enable the about page functionality in the angular seed advance project?

After carefully following the instructions provided on this page https://github.com/NathanWalker/angular-seed-advanced#how-to-start, I successfully installed and ran everything. When running npm start, the index page loads with 'Loading...' I ha ...

Error: Navbar component cannot access static member

I am encountering the error message Static member is not accessible at this particular point: https://i.sstatic.net/LgeAa.png auth.service.ts export class AuthService { public static get isLoggedIn() { const user = JSON.parse(localStorage.getItem( ...

Ways to filter and display multiple table data retrieved from an API based on checkbox selection in Angular 2 or JavaScript

Here is a demonstration of Redbus, where bus data appears after clicking various checkboxes. I am looking to implement a similar filter in Angular 2. In my scenario, the data is fetched from an API and stored in multiple table formats. I require the abili ...

Angular 2: Assigning a class to a D3 element using the component's style

When creating a component in Angular 2, the `app.component.css` file defines a class called `h-bar`: https://i.sstatic.net/AG1ER.png In the `app.component.ts` file, d3 is utilized to create elements that should apply the `h-bar` class from the `app.compo ...

Implementing a global provider in Ionic 3

I have integrated a provider in my app that needs to stay active at all times while the application is running to monitor the network connection status. Following this guide, I included the class in my app.module.ts file to ensure it functions as a global ...

Navigating URLs to index.html for localized Angular application within lighttpd server - a guide

When deploying an Angular application to a lighttpd server, if a user is browsing example.com/product/12 and sends the link to someone else, they may encounter a 404 error without proper URL rewriting. In this scenario: localized versions are stored in s ...

The number pipe is unable to interpret the given value, either because it is outside of the allowable range or cannot be

An error occurred while processing the specified value, it seems to be either unparsable or out of the acceptable range. Whenever I apply formatting with a pipe to a number in my object, I encounter this warning and the value fails to display. However, on ...

Guide on Importing All Functions from a Custom Javascript File

As a beginner in learning Angular, I am faced with the task of converting a template into Angular. However, I am struggling to find a solution for importing all functions from a custom js file into my .component.ts file at once. I have already attempted t ...

Experimenting with viewProvider in a component test

@Component({ selector: 'app-entity-details', templateUrl: './entity-details.component.html', styleUrls: ['./entity-details.component.scss'], viewProviders: [{ provide: ControlContainer, useFactory: (container: ...

Guide to accessing and showcasing an embedded Firestore entity using Angular?

I'm looking to showcase a Firestore model with profile names and hashtags using Angular 6. As I delve into Firestore, I've discovered that the proper way to model it is like this: enter image description here members { id: xyz ...

Tips for separating provider and input components with React Hook Form

Currently, I am working on a project with Next 13.5 using react-hook-form and shadcn-ui. The issue that I have encountered is that creating a form involves too much redundant code. To address this, I abstracted the FormProvider and Input component. The pr ...

Using Angular to import a JSON file stored locally

I am facing an issue with importing a .json file in my Angular project. The file is located outside the project structure as shown below: | common | configuration.json | angular-app | src | app | my-component | ...