Reactive form within a parent object for nested counting

I am looking to generate a nested form based on the following data:

The current data available is as follows:

mainObject = {
  adminname: 'Saqib',
  adminemail: '<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="40252d21292c002538212d302c256e232f2d">[email protected]</a>',
  users: [
    { user_type: 'Adult', count: 3 },
    { user_type: 'Child', count: 1 },
    { user_type: 'Infant', count: 0 },
  ],
};

I aim to display nested forms for the counts mentioned in the Users Array. For instance, there should be 3 adult forms, 1 child form, and no Infant form as the Infant count is 0. These counts are flexible and can be adjusted, such as having 0 child forms and 1 infant form.

.html

<form [formGroup]="myForm" (ngSubmit)="onSubmit()">
  <input formControlName="name" placeholder="Enter Admin Name" />
  <br />
  <input formControlName="admin_email" placeholder="Enter Admin Email" />

  <div formArrayName="users"gt;
    <div *ngFor="let pro of myForm.get('users').controls; let i = index">
      <br />
      <div [formGroupName]="i" style="border: 1px solid; padding: 10px">
        <div>User Type: {{ pro.value.user_type }}</div>
        User Name:<br />
        <input type="text" formControlName="user_name" /> <br />

        Email:<br />
        <input type="text" formControlName="user_email" />
      </div>
    </div>
  </div>
  
  <p>
    <button type="submit">Submit</button>
  </p>
</form>

.ts

myForm: FormGroup;

  mainObject = {
    adminname: 'Saqib',
    adminemail: '<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="f99c94989095b99c81989489959cd79a9694">[email protected]</a>',
    users: [
      { user_type: 'Adult', count: 3 },
      { user_type: 'Child', count: 1 },
      { user_type: 'Infant', count: 0 },
    ],
  };
  // Generating forms based on user counts and types mentioned in the data.

  constructor(private fb: FormBuilder) {}

  ngOnInit() {
    this.formInit();
  }

  formInit() {
    this.myForm = this.fb.group({
      name: '',
      admin_email: '',
      users: this.fb.array(
        this.mainObject.users.map((e) => this.newUsers(e.user_type, e.count))
      ),
    });
  }

  get users(): FormArray {
    return this.myForm.get('users') as FormArray;
  }

  newUsers(t, c): FormGroup {
    console.log(t, c);
    return this.fb.group({
      user_type: t,
      user_name: '',
      user_email: '',
    });
  }

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

For a clearer understanding, I have created an example on Stackblitz:

Example Stackblitz

Answer №1

It is possible to implement the following concept:

Approach:

  1. Start by creating a dictionary where keys are grouped by user_type and values are arrays with object(s) based on the count.

  2. Extract the values from the first result, flatten the nested array, and add the objects to a new array.

toFormUsers(users: any[]): any[] {
  // Create array by user_type   
  let userTypeGroup = users.reduce((accumulator, current) => {
    accumulator[current.user_type] = accumulator[current.user_type] || [];

    for (let i = 0; i < current.count; i++)
      accumulator[current.user_type].push({ user_type: current.user_type });

    return accumulator;
  }, {});

   // Flatten array of arrays for userTypeGroup
  return [].concat(
    ...Object.keys(userTypeGroup).map((x) => userTypeGroup[x])
  );
}

Update your FormGroup to incorporate the method by passing mainObject.users.

this.myForm = this.fb.group({
  name: '',
  admin_email: '',
  users: this.fb.array(
    this.toFormUsers(this.mainObject.users).map((e) => this.newUsers(e.user_type, e.count))
  ),
});

View a Sample Demo on StackBlitz

Answer №2

To generate a nested array of counts within the users array sourced from the mainObject:

.ts

ngOnInit() {
    this.initializeForm();
  }

  initializeForm() {
    this.myForm = this.fb.group({
      name: '',
      admin_email: '',
      users: this.fb.array([]),
    });

    this.mainObject.users.map((element) => {
      if (element.count > 0) {
        for (let i = 0; i < element.count; i++) {
          this.addUser(element.user_type);
        }
      }
    });
  }

  addUser(type) {
    const userArray = <FormArray>this.myForm.controls['users'];
    const newUser = this.createUser(type);
    userArray.push(newUser);
  }

  createUser(type) {
    return this.fb.group({
      user_type: type,
      user_name: '',
      user_email: '',
    });
  }

  get users(): FormArray {
    return this.myForm.get('users') as FormArray;
  }

  newUsers(type, count): FormGroup {
    console.log(type, count);
    return this.fb.group({
      user_type: type,
      user_name: '',
      user_email: '',
    });
  }

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 best way to incorporate a progress bar animation into my notification?

Seeking assistance to implement an animated progress bar that changes colors gradually over time based on a variable [timer]. Can anyone lend a hand with this? Thank you! https://i.sstatic.net/lhgeF.png $(document).ready(function(){ window.addEvent ...

Preserve the previous and current state within a React application

Within my code, there is a state called newuser that undergoes changes based on the inputs entered into the input fields. This change occurs once all input fields are filled and the submit button is clicked. The goal I have in mind is to store the current ...

The attribute 'id' cannot be found in the class 'Foods'

After examining my code below, I am attempting to remove clients from my data table by using checkboxes. Whenever I check a checkbox, I notice in the console that the object's properties are retrieved from the database. My goal is to delete by id, but ...

The type 'xxxx' is not compatible with the parameter type 'JSXElementConstructor<never>'

I am currently enrolled in a TypeScript course on Udemy. If you're interested, you can check it out here. import { connect } from 'react-redux'; import { Todo, fetchTodos } from '../actions'; import { StoreState } from '../red ...

Type of Angular Service Issue: string or null

I'm encountering a persistent issue with my Angular code, specifically while calling services in my application built on Angular 13. The problem arises when trying to access the user API from the backend, leading to recurrent errors. Despite extensive ...

Is there a way to automatically adjust the positioning of added pins on an image as I scroll through the image?

I have inserted a large image onto my HTML page and to manage its size, I am displaying it within a div that allows for scrolling like a map. Using jQuery, I have placed 3 markers on the image. The issue I am facing is that when I scroll the image, the ma ...

Troubleshooting issues with Bootstrap's responsiveness configuration

I've been working on creating a responsive user login page using Angular 5. It looks great on full-screen display. https://i.sstatic.net/YQrL5.png However, when I resize the browser window, the responsiveness seems to break. https://i.sstatic.net/4 ...

How can I access an InputStream from a local XML file in a PhoneGap application?

Looking for advice on how to fetch an inputstream from a local XML file using JavaScript in my PhoneGap application. I'm new to JavaScript, so any guidance would be appreciated! ...

Increasing the value of a food topping element within a v-for list of toppings when clicking on the "+ add" button in Vue

I'm a newcomer to the world of JavaScript and Vue.js, currently working on a project to automate the ordering process for my pizza delivery pizzeria. On the website, I have a list of toppings that customers can choose from. They have the option to se ...

The Angular 2 rollup AoT compilation results in a larger build size compared to the standard JiT build

I'm facing an issue with reducing the weight of my app during the building process. I am using Angular 2 seed as a starting point. https://github.com/mgechev/angular-seed When I run: npm run build.prod my app.js file size is 1.5MB. However, when I ...

offspring of offspring in jquery animation remains stationary

I'm experiencing an issue with a jquery animation on containers that are set to 100% width and height. Specifically, the children elements that have position absolute move with the container, but when a child of a child has two instances of position a ...

The functionality of jQuery Validation is not working properly once the form has been loaded using an AJAX request

I've encountered an issue with form validation in a lightbox that is loaded after submitting a previous form via ajax. The jQuery validation on the new form works by default, but it does not trigger my custom validation function for checking if the pa ...

jquery is showing up in the browserify bundle.js file, however, it is not functioning properly

Currently, I am trying to follow a brief tutorial on how to use Browserify. Despite following the instructions precisely, jQuery seems to not be working properly when bundled. Specifically, the button element in my app.js code is not appended to the body. ...

Using the "i" parameter in a Lodash for loop results in undefined, however, it functions properly with specific values set

My goal is to use Lodash to search for specific integer values in an object and then store some of those values in an array. The integers are variable and come from a separate array, but I am consistently getting undefined as the result. If I manually inp ...

Stop users from submitting empty forms

I'm facing an issue with my form where I want to prevent it from being submitted if the fields are blank and also highlight those blank fields. The code I currently have works when trying to submit with blank fields, but for some reason, it doesn&apos ...

Having trouble retrieving a customized header from the HTTP response

I've encountered an issue with my Node.js server where I set a custom header. I added the Access-Control-Expose-Headers to allow access from browsers, and it works fine in Chrome and Firefox. However, I'm getting an error in PhantomJS saying "Ref ...

Sending a Boolean value from a child component to its parent state in React JS

Within my application, I have implemented a login feature in the navbar component. However, I am encountering an issue with updating a variable in the main component upon successful login. Although I have successfully managed to set up the login functional ...

Should we store $(this) in jQuery's cache, or leave it be?

When dealing with a selector such as $(this), does the act of creating and reusing a reference actually have a noticeable impact on performance? I find it more efficient to create references for jQuery selectors that are used repeatedly within the same sc ...

Conceal the PayPal Button

Currently, I'm facing a challenge where I need to dynamically show or hide a PayPal button based on the status of my switch. The issue is that once the PayPal button is displayed, it remains visible even if the switch is toggled back to credit card pa ...

Javascript retrieve the style of an element in every possible state

I am interested in retrieving the line height of an element; it's a simple task. Here is a method that I know works: console.log(document.getElementById("myDiv").style.lineHeight); console.log($("#myDiv").css('lineHeight')) ...