Creating a dynamic tab component in Angular using data from an API

I have received the following JSON response:

Based on this JSON response, I am creating dynamic tabs and within each tab, I want to push a formArray based on specific conditions mentioned below.

**In the response below,

 const myObj = [
          {
            'TabName': 'Test1',
            'otherDetails': [
              {
                'processTechType': 'Continuous'
              },
              {
                'processTechType': 'Batch',
              },
            ]
          },
          {
            'TabName': 'Test2',
            'otherDetails': [
              {
                'processTechType': 'Batch'
              }
            ]
          }
        ];

For example -** The tab names Test1 and Test2 are dynamically displayed as tabs. In Test1 Tab, both forms named Continuous and Batch should be pushed. This is because in Test1 Tab, the processTechType array includes both Continuous and Batch types. Therefore, both forms will be shown in the Test1 Tab.

Example 2 -- In the Test2 Tab, only the Batch form should be pushed. This is because in the Test2 Tab, the otherDetails object contains processTechType batch. Therefore, only the Batch form will be shown in the Test2 Tab.

This means that it will check the tab name and otherDetails key from the response and display forms based on the processTechType array on specific tabs only.

I have the code below. However, it currently pushes both forms into all tabs instead of specific tabs. For example - The current code displays the Continuous formArray once and the Batch formArray twice in both Test1 and Test2 tabs.

Expected output -

In the Test1 Tab, one Continuous form and one Batch form will be pushed.

In the Test2 Tab, only the Batch form will be displayed.

Can anyone please help me adjust my code to achieve the expected output.

getMakeLineData() {
    
    var otherDetails = myObj.filter(m => m.otherDetails).map(m => m.otherDetails);
    this.makeLineData = myObj;
    if (otherDetails) {
      otherDetails.forEach(element => {       
        for (var i of element) {
          if (i.processTechType === 'Continuous') {
            this.addQuantity();
          }
          else if (i.processTechType === 'Batch')  {
            this.addBatch();
          } 
        }
      });      
    } 
}

createContinuousForm() {
    return this.fb.group({
      designProcess: ['', [Validators.required]]
    });
  }
  createBatchForm() {
    return this.fb.group({
      avgBCT: ['', [Validators.required]]
    });
  } 
  continuousType(): FormArray {
    return this.dataCollectionForm.get("continuousType") as FormArray;
  }

  batchType(): FormArray {
    return this.dataCollectionForm.get("batchType") as FormArray;
  }

  addQuantity() {
    this.continuousType().push(this.createContinuousForm());

  }
  addBatch() {
    this.batchType().push(this.createBatchForm());
  }

HTML form template

<div class="tabGroupDiv row">
    <div class="lossLinesDiv">     
      <mat-tab-group class="lossMatGrpCls" mat-align-tabs="left">
        <mat-tab *ngFor="let lineData of makeLineData">
          <ng-template mat-tab-label>
                <button class="validatorTabBgClr">{{lineData.makeLineName}}</button>
          </ng-template>
          <form [formGroup]="dataCollectionForm" (ngSubmit)="onSubmit()">
            <!-- <--continuous Form start here -->  
            <div class="admin-console-main-wrapper" formArrayName="continuousType">
              <div class="content-wrapper" *ngFor="let lineItem of continuousType().controls; let i=index"
                [formGroupName]="i">
              
                <div class="row list-wrapper">
                  <div class="col-xs-3 col-md-3 deo-dv-list-wrapper">
                    <h5 class="topbar-items-text">Design Process Capacity (Tonnes)</h5>
                    <mat-form-field appearance="outline">
                      <input matInput type="text" class="line-fte-input smed-input" placeholder="Design Process Capacity"
                        formControlName="designProcess">
                    </mat-form-field>
                    <mat-error *ngIf="lineItem?.controls?.designProcess?.hasError('required')">
                      Field is required
                    </mat-error>
                  </div>
                </div>              
              </div>
            </div>  
            <!-- <--continuous Form end here -->
  
            <!-- <--Batch Form start here -->
            <div class="admin-console-main-wrapper" formArrayName="batchType">
              <div class="content-wrapper" *ngFor="let lineBatchItem of batchType().controls; let i=index"
                [formGroupName]="i">               
  
                <div class="row list-wrapper">
                  <div class="col-xs-3 col-md-3 deo-dv-list-wrapper">
                    <h5 class="topbar-items-text">Average BCT (mins)</h5>
                    <mat-form-field appearance="outline">
                      <input matInput type="text" class="line-fte-input smed-input" placeholder="Average BCT"
                        formControlName="avgBCT">
                    </mat-form-field>
                  </div>                 
                </div>
              </div>
            </div>  
            <!-- <--Batch Form ends here -->
          </form>
        </mat-tab>
      </mat-tab-group>
    </div>
  </div>

Answer №1

Utilizing a single dataCollectionForm structure for multiple tabs, each requiring its form, will not function properly without recreating the form upon tab change.

  1. dataCollectionForm should consist of form groups identified by unique IDs.
  2. Avoid unnecessary complexity and incorrect usage of mapping functions.

The sudo code provided below is intended to guide you in the right direction:

 public makeLineData: any[] = []; // contains otherDetails

   // holds forms based on myObj index
   public dataCollectionForm: FormGroup[] = []; // iterate over this in the template using an index

 createForm() { // name correction required for getMakeLineData

    myObj
   .filter(m => m.otherDetails)
   .forEach((obj) => {
     // create and add Form to dataCollectionForm
     this.makeLineData.push(obj);
     let dataFrom =  this.addTabDataCollectionForm();

     obj.otherDetails.forEach((detail) => {
       if (detail.processTechType === 'Continuous') {
            this.addQuantity(dataFrom);
          }
          else if (detail.processTechType === 'Batch')  {
            this.addBatch(dataFrom);
          } 
     })

   
   })

    var otherDetails = myObj.filter(m => m.otherDetails).map(m => m.otherDetails);
    this.makeLineData = myObj;
    if (otherDetails) {
      otherDetails.forEach(element => {       
        for (var i of element) {
          if (i.processTechType === 'Continuous') {
            this.addQuantity();
          }
          else if (i.processTechType === 'Batch')  {
            this.addBatch();
          } 
        }
      });      
    } 
}

addTabDataCollectionForm (): FromGroup {
  // represents the form of a single tab
  let tabDataForm = this.fb.group({
      continuousType: new FormArray([])
      batchType: new FormArray([])
    });

    this.dataCollectionForm.push(tabDataForm); 
    return tabDataForm; 
    
}

createContinuousForm() {
    return this.fb.group({
      designProcess: ['', [Validators.required]]
    });
  }
  createBatchForm() {
    return this.fb.group({
      avgBCT: ['', [Validators.required]]
    });
  } 

  continuousType(dataFrom): FormArray {
    return dataFrom.get("continuousType") as FormArray;
  }

  batchType(dataFrom): FormArray {
    return dataFrom.get("batchType") as FormArray;
  }

  addQuantity(dataFrom) {
    this.continuousType(dataFrom).push(this.createContinuousForm());

  }
  addBatch(dataFrom) {
    this.batchType(dataFrom).push(this.createBatchForm());
  }

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

What is the reason behind having to include index 1 in the childNodes array when trying to access the initial child element?

I'm working on a JavaScript function that is supposed to display the text entered into an input field. Here is the code I have written: <head> <script type="text/javascript"> function showText() { var input = document. ...

Transmitting special symbols through Socket.io

I've been working on a small project based on Socketio 0.9 and everything is running smoothly, except for a minor problem with special characters. In the web client, I am creating a dynamic JSON object using JavaScript that is then emitted to the ser ...

"Enhance your React application with react-router-dom by including parameters in the index URL while also

Here is the code I am currently using: <Router history={history}> <div> <Route exact path="/:id?" component={Container1} /> <Route path="/component2/ component={Container2} /> </div> </Router> When accessin ...

Strategies for reversing strings in JavaScript without relying on built-in functions

Are you wondering how to reverse the words in a string without using the split, reverse, and join functions? Here is the problem: You need to reverse the words in a string. For example, if the input is "Hello World," the output should be "World Hello." ...

It appears that the font in style.css is not being updated properly and seems to resemble

My issue lies within my CSS code. The text on my website is not displaying correctly and seems to be ignoring the styling that I have applied. Even though I have used similar styling on other buttons which look fine, this specific text element is causing p ...

Is it necessary to incorporate Babel into a project that involves developing a backend service using Node.js and a front-end component using the EJS view engine?

I find myself a little confused. Some say that if you are working on pure Node.js projects, there is no need to stress about this issue. However, for web development, it's important to be familiar with these tools. On the other hand, some recommend us ...

jQuery News Ticker - Twitter-esque vibes!

I am searching for a news ticker similar to the one found on Twitter that scrolls horizontally. The key features I need: Continuous scrolling - Elements should be removed when they are no longer visible and placed at the end of the list. No Pausing ...

Resetting Laravel passwords without relying on user emails, with the additional use of Angular for the front end interface

I'm currently working on a laravel-angular application that requires a password reset feature. However, the default password-reset in Laravel relies on email verification, which is not necessary for this particular application. I tried setting $confir ...

Accessing a template object in JavaScript using querySelector

Essentially, I am trying to querySelect a <template> from JavaScript but constantly receiving null as the result. JavaScript code: class MyImportWebcomponent extends HTMLElement { constructor() { super(); } connectedCallback() { ...

Is it possible to access the $pristine property of an input element in Angular without using a form?

Imagine having the following HTML... <div class="row"> <div class="col-xs-3 align-right"> <p>Name</p> </div> <div class="col-xs-7"> <input type="text" class="form-control" ng-model="registrati ...

Using JSP to send variables from an external Javascript file

After creating a timer function, I am looking to display the results on a different page. The setup involves a JSP file calling functions from a separate JS file in order to output the information to another screen: Instructions in the JSP file: <butt ...

PHP code isn't properly redirecting to AJAX calls

I have the following JavaScript code that validates a form by calling a PHP script on the backend: $(function() { // Setting up form validation for the #register-form element $("#register_form").validate({ // Specifying the validation ru ...

What is causing the components to not re-render after a state change in React?

I am attempting to retrieve data from the Coin Market Cap API and showcase the details in simple cards. I have experimented with various methods and included comments to document my trials. Any assistance would be greatly appreciated. This is the Parent: ...

Module ng2-img-tools encountered a metadata version mismatch error

Encountering an error while trying to utilize the ng2-img-tools package in conjunction with Angular4. The specific error message is: ERROR in Metadata version mismatch for module ~/client/node_modules/ng2-img-tools/dist/ng2-img-tools.d.ts, found version 4 ...

Retrieve the current language from a non-page component on the server side with the help of Next.js and the next-i18next

I am working on a nextjs application that utilizes the next-i18next library for internationalization. I'm struggling to figure out how to access the current language on the server-side within a component that is not a page. On the server side, you ca ...

Error: The object #<HTMLCollection> does not support the 'tags' method

While trying to troubleshoot this issue, it seems like I haven't been able to pinpoint the simple solution. This particular javascript menu works perfectly in IE, however in Chrome, there is an error when trying to open the menu with a first-level cli ...

Tips for accessing images in Angular 24 lazy loaded modules

I have a collection of images that need to be shared throughout my application. There are 2 lazy-loaded modules - Login and Overview. The image I require is located in src/assets/images/logo.png and needs to be accessible in both the Login and Overview com ...

Issues arise when attempting to use AngularJS validators in conjunction with another directive

I created a directive called "uniqueCheck" that validates the ngModel based on whether the value is already in a list or not. Everything works fine when the directive is used directly on an input tag. However, when used on a custom directive that contains ...

Save document to a directory within a Cordova Hybrid application

Scenario: My current project involves developing a hybrid app using Cordova 6 and SAPUI5 Framework (mainly focused on Android at the moment). Objective: I need to quickly copy/move a file to a specific path. This may involve retrieving a FileEntry from a ...

How can we implement infinite scrolling with a JSON array using Javascript?

Looking at my JavaScript snippet: items.forEach(function (item, index, arr) { console.log(item.price); var message = 'BitSkins Price: $' + item.bprice + ''; if (item.price != null) { ...