1

In angular 5 I have made the functionality for add new row and remove row by using formarray. Its working fine for now. But lets say I have some data and I want to show them in the rows one by one so lets say I have data for 3 rows so the form should automatically show 3 rows by default with all the values populated in it with add and remove row functionality.

This is the code what I have done so far

app.component.html looks like this

<div class="container col-md-12">
  <h1 class="page-header">Create Event</h1>
  <div class="row show-hide-message">
    <div [ngClass]= "messageClass">{{message}}</div>
  </div>
  <form [formGroup] = "form" (ngSubmit)="onEventSubmit()">
  <fieldset>
    <div class="form-group">
      <label for="eventname">Event Name</label>
      <div class='form-group' [ngClass]="{'has-error': form.controls.eventname.errors && form.controls.eventname.dirty, 
  'has-success': !form.controls.eventname.errors
}">
        <input type="text" class="form-control" autocomplete="off" placeholder="Event Name" formControlName="eventname">
        <ul class="help-block">
          <li *ngIf="(form.controls.eventname.errors?.minlength ) && form.controls.eventname.dirty">Event name should be atleast 5 characters</li>
        </ul>
      </div>
    </div>


    <h4>Package Price</h4>
    <hr>
    <div class="row" formArrayName="sections">
      <div class="col-md-12" *ngFor="let section of getSections(form); let i = index" [formGroupName]="i">
        <div class="form-group col-md-5">
          <label for="packagename">Package Name</label>
          <input type="text" class="form-control" autocomplete="off" placeholder="Package Name" formControlName="packagename"  >
       </div>
        <div class="form-group col-md-2">
          <label for="packageprice">Package Price</label>
          <input type="number" class="form-control" autocomplete="off" placeholder="Package Price" formControlName="packageprice" >
        </div>
        <div class="form-group col-md-2">
          <label for="packagelimit">Max Purchase Limit</label>
          <input type="number" class="form-control" formControlName="packagelimit" autocomplete="off" >
        </div>
        <div class="form-group col-md-1">
          <br/>
          <input type="button" (click)="addPackageRow()" class="btn btn-md btn-success" value="+" name="">
        </div>
        <div class="form-group col-md-1" *ngIf="getSections(form).length > 1">
          <br/>
          <input type="button" (click)="removeSection(i)" class="btn btn-md btn-error" value="-" name="">
        </div>
      </div>
    </div>

    <input [disabled]=!form.valid type="submit" class="btn btn-primary" value="Submit">

    <pre>{{form.value | json}}</pre>

  </fieldset>
</form>
</div>

app.component.ts looks like this

import { Component, OnInit } from '@angular/core';
import { FormArray, FormGroup, FormControl, Validators, FormBuilder } from '@angular/forms';
@Component({
  selector: 'my-app',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit {
  form :  FormGroup;
  packagesArray: FormArray;

  storageData = [
    {"packagename":"Package One","packageprice":12,"packagelimit":9},
    {"packagename":"Package Two","packageprice":78,"packagelimit":5},
    {"packagename":"Package Three","packageprice":89,"packagelimit":7}
];

  constructor(
    private formBuilder : FormBuilder,
    ) { this.createEventForm() }

  createEventForm() {
    this.form = this.formBuilder.group({
      eventname: ['', Validators.compose([
        Validators.required,
        Validators.minLength(5)
      ])],
      packages: this.packagesArray
    })
  }

  ngOnInit() {
    this.form = new FormGroup({
      eventname: new FormControl(''),
      sections: new FormArray([
        this.initSection(),
      ]),
    });
  }

  initSection() {
    return new FormGroup({
      packagename: new FormControl(''),
      packageprice: new FormControl(''),
      packagelimit: new FormControl('')
    });
  }

  initItemRows() {
        return this.formBuilder.group({
            itemname: ['']
        });
    }

  onEventSubmit() {}

  public addPackageRow() {
    const control = <FormArray>this.form.get('sections');
    control.push(this.initSection());
  }


  addSection() {
    const control = <FormArray>this.form.get('sections');
    control.push(this.initSection());
  }

  getSections(form) {
    return form.controls.sections.controls;
  }

  public removeSection(i){
   const control = <FormArray>this.form.get('sections');
   control.removeAt(i);
  }

}

So can someone tell me how to do this? Any help and suggestion will be really appreciable. Thanks

I just want to populate data of storageData with the respective rows.

Here is the working demo

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

5
  • Don't you want to say "data" in your title ? Commented Feb 14, 2018 at 7:09
  • If I understand, you want an animation on your ngFor to show rows one by one (for the 3 first elements but if you add multiple rows at once too) ? Commented Feb 14, 2018 at 7:11
  • actually I want to populate the stored values in the formarray with add row and remove row functionality. It does not matter there will be 3 rows in the data there might be any number of data. Commented Feb 14, 2018 at 7:15
  • I have changed the title. Thanks for pointing typo error. Commented Feb 14, 2018 at 7:16
  • If you want to add a control : Use addControl function of your formGroup because controls attribute is readonly Commented Feb 14, 2018 at 7:22

3 Answers 3

2

I would go with:

this.form = new FormGroup({
  eventname: new FormControl(''),
  sections: new FormArray(this.storageData.map(item => {
    const group = this.initSection();
    group.patchValue(item);
    return group;
  }))
});

Ng-run Example

Sign up to request clarification or add additional context in comments.

Comments

1

Change your ngOnInit :-

    ngOnInit() {
    this.form = new FormGroup({
      eventname: new FormControl(''),
      sections: new FormArray([
        this.initSection(),
      ]),
    });

    var data;
    for(data in this.storageData){
      console.log(this.storageData[data]);
      var temp = {};
      for(var k in this.storageData[data]){
        //console.log(k);
        temp[k] = new FormControl(this.storageData[data][k]);
      }
      console.log(temp);
      var formGrp = new FormGroup(temp);

      const control = <FormArray>this.form.get('sections');
      control.push(formGrp);
    }
  }

3 Comments

actually it is making the first row empty.
Yes. So it should fill the data of first row in that html
if you are creating all the row dynamically then you should not hardcode row in html page.
1

puff, I suggested make a functión that return the formArray

//this.fb is in the constructor private fb:FormBuilder
getSeccion(storageData: any[]): FormArray {
    //create an arr of FormGroup
    //each element of array storageData transform in a FormGroup
    const arr = storageData.map(data => {
        return this.fb.group({
            packagename: [data.packagename,Validators.Required], //<--e.g. if Required
            packageprice: [data.packageprice],
            packagelimit: [data.packagelimit]});
    })
    //return a FormGroup with this array
    return this.fb.array(arr);
}

So, your ngOnInit

ngOnInit() {
    this.form = new FormGroup({
      eventname: new FormControl(''),
      sections: this.getSeccion(this.storageData )
    });

MoreOver, if you has an object

mydata={eventname:'myEvent',
        sections:[{packagename:'name1',packageprice:'100',packagelimit:30},
        {..},
        {..}..]

You can use

this.form = new FormGroup({
      eventname: new FormControl(this.myData.eventname),
      sections: this.getSeccion(this.myData.storageData )
    });

Well, normally we get data from database. So our ngOnInit can be some like

ngOnInit()
{
    myservice.getData().subscribe((res:any)=>{
          this.data=res  //normally we can save the data in a variable
          this.form = new FormGroup({
              eventname: new FormControl(res.eventname),
              sections: this.getSeccion(res.storageData )
           });
    })
}

2 Comments

what would happen If I get data from database. At that moment I would not get data on ngOnInit() function. So how to access data at that moment and populate it?
I updated the answer to include when we get data from dataBase. In this case our form is simply (has only two fields eventname and sections, if has more fileds we can make a function to create all the form -yes, this function call the function this.getSeccion

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.