0

I have the following html in my component, Im trying to generate a form dynamically based on the number of selected elements that can be from 0 to N

<form #form="ngForm" id="formGroupExampleInput">
  <div class="col-xs-5 col-md-offset-1">
    <a class="list-group-item clearfix" *ngFor="let selectedApi of selectedApps;let i=index">
      <div class="pull-right">
        <button type="button" class="close" (click)="removeFromSelectedApi(i)">
          <span aria-hidden="true">&times;</span>
        </button>
      </div>
      <div class="pull-left">
        <h4 class="list-group-item-heading">{{selectedApi.url}}</h4>
        <p class="list-group-item-text">{{selectedApi.method}}</p>
        <div class="form-group">
            <input type="text" class="form-control" id="formGroupExampleInput" name="{{i}}-users"
            placeholder="number of users" ngModel>
            <input type="text" class="form-control" id="formGroupExampleInput" name="{{i}}-rpm"
            placeholder="request per minute between all users" ngModel>
        </div>
        <hr>
        <div class="form-group" *ngFor="let requiredHeader of selectedApi.requiredHeaders; let in=index">
          <input type="text" class="form-control" id="formGroupExampleInput" name="{{i}}-{{requiredHeader}}"
            placeholder={{requiredHeader}} ngModel>
        </div>
        <div class="form-group" *ngFor="let requiredParameter of selectedApi.requiredParametersInURL; let in=index">
            <input type="text" class="form-control" id="formGroupExampleInput" name="{{i}}-{{requiredParameter}}"
              placeholder={{requiredParameter}} ngModel>
          </div>
        <div class="form-group" *ngIf="selectedApi.method=='POST' || selectedApi.method=='PUT' || selectedApi.method=='DELETE'">
          <!-- <textarea class="form-control" id="formGroupExampleInput" name="{{i}}-{{requiredHeader}}" rows=20></textarea> -->
          <textarea  class="form-control"
                rows="5"
                name="{{i}}-body" 
                id="{{i}}-body" 
                placeholder="body" ngModel></textarea>
        </div>
      </div>
    </a>
  </div>
  <div class="pull-right">
    <button class="btn btn-success btn-lg" (click)="onSubmitTest(form)">Submit</button>
  </div>
</form>

when I create new elements and submit the form I get as a result on form.value something like this:

0-auth-system: "c"
0-auth-user: "d"
0-auth-user-id: "e"
0-email: "h"
0-module: "g"
0-rpm: "b"
0-task-client-key: "f"
0-users: "a"
1-auth-system: "k"
1-auth-user: "l"
1-auth-user-id: "m"
1-rpm: "j"
1-task-client-key: "n"
1-users: "i"

and what I want in fact is an output like the following since it is easier for processing and I dont want to develop code to parse to the previous structure if I can have it as an array:

[
 {
  auth-system: "c"
  ,auth-user: "d"
  ,auth-user-id: "e"
  ,email: "h"
  ,module: "g"
  ,rpm: "b"
  ,task-client-key: "f"
  ,users: "a"
},
{
  auth-system: "k"
  ,auth-user: "l"
  ,auth-user-id: "m"
  ,rpm: "j"
  ,task-client-key: "n"
  ,users: "i"
 }
]

Anyone knows how I can achieve this?

4 Answers 4

2
   <form [formGroup]="myFormUnit" (ngSubmit)="submit()">
        <table class="table table-lessons table-striped table-hover">
          <thead>
          <tr>
            <th></th>
            <th>Field A</th>
            <th>Field B</th>
          </tr>
          </thead>
          <tbody>
          <tr *ngFor="let item of myFormUnit.get('items').controls; let i=index" [formGroup]="item">
            <td>
              <input type="text" class="form-control lablel border-less" [attr.id]="'name'+i" formControlName="name"
                     disabled="disabled">
              <input type="number" class="form-control hidden" readonly="readonly" hidden="hidden"
                     [attr.id]="'grade_list'+i" formControlName="grade_list"
                     style="visibility: hidden;">
            </td>
            <td>
              <input type="number" class="form-control" [attr.id]="'min_grade'+i" formControlName="min_grade">
            </td>
            <td>
              <input type="number" class="form-control" [attr.id]="'max_grade'+i" formControlName="max_grade">
            </td>
          </tr>
          </tbody>
        </table>
      </form>

and ts file is :

 myFormUnit: FormGroup;
  protected onInitializeComponent() {
    this.myFormUnit = this.fb.group({
      items: this.fb.array([])
    });
  }
  private fillUnitform(input?: any) {
    this.myFormUnit = this.fb.group({
      items: this.fb.array(
        []
        // [this.buildItem(null, 'lesson'), this.buildItem(null, 'lesson')]
      )
    });
    this.gradeListItems.forEach(item => {
      if (item.value !== "12") {
        const tmp: Array<any> = this.gridData.data;
        tmp.forEach(element => {
          if (element.lesson_type === item.value) {
            item.value = element.num_unit;
          }
        });
        const fbb = this.myFormUnit.get("items") as FormArray;
        if (item.value !== "12") {
          const newItem = {
            name: item.text,
            grade_list: item.value,
            min_grade: item.min_grade,
            max_grade: item.max_grade
          };
          if (input) {
            // if (input.length > 0)

            input.forEach((element: any) => {
              if (element.grade_list === item.value) {
                newItem.min_grade = element.min_grade;
                newItem.max_grade = element.max_grade;
              }
            });
          }

          fbb.push(this.fb.group(this.buildItem(newItem)));
        }
      }
    });
  }
  buildItem(item: any) {
    return {
      name: new FormControl(item.name),
      grade_list: new FormControl(item.grade_list),
      min_grade: new FormControl(item.min_grade),
      max_grade: new FormControl(item.max_grade)
    };
  }

submit(){
        this.myFormUnit.value.items.forEach(
          (item): any => {
            if (item.min_grade || item.max_grade) {
              grade_list.push({
                grade_list: item.grade_list,
                min_grade: item.min_grade,
                max_grade: item.max_grade
              });
            }
          }
        );

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

1 Comment

mosi98, you needn't create a formGroup with a formArray, you can use a formArray directly -see my answer-
1

I had a similar problem.

Check out this site

I'm sure it'll give you an idea if you read it carefully

2 Comments

While sharing a link might be helpful, it's not an answer. Answers should contain an explanation instead of pure links or code
correct but if the link is followed it'll provide an answer, the link directs to a tutorial that explains in detail. What is the point of me explaining it if the link already does? Repetition is not necessary
0

@mosi98, you needn't create a formGroup with property items, and "items" was a formArray

@Component({
  selector: 'my-app',
  template:`
  <!--see that, form is a FormArray, but we use [formGroup] too-->
  <form [formGroup]="form">
    <div *ngFor="let group of form.controls;let i=index">
        <div [formGroup]="group">
            <input formControlName="name"/>
            <input formControlName="grade_list"/>
      </div>
    </div>
  </form>
  <hr/>
  <pre>
   {{form?.value|json}}
  </pre>
  `,
  styleUrls: [ './app.component.css' ]
})
export class AppComponent  {
  name = 'Angular';
  items=[
  {name: "c",grade_list: "d",min_grade: "e",max_grade: "h"},
  {name: "c",grade_list: "d",min_grade: "e",max_grade: "h"},
  {name: "c",grade_list: "d",min_grade: "e",max_grade: "h"}]

  form:FormArray=new FormArray(this.buildForm(this.items))

  //return an array of FormGroup
  buildForm(items:any[]):FormGroup[]
  {
    return items.map(x=>this.buildItem(x))
  }
  //return a formGroup
  buildItem(item: any):FormGroup {
    return new FormGroup({
      name: new FormControl(item.name),
      grade_list: new FormControl(item.grade_list),
      min_grade: new FormControl(item.min_grade),
      max_grade: new FormControl(item.max_grade)
    });
  }
}

2 Comments

the items in the array are dynamic, they can be from 1 to n<100, so I cannot create a class like that for items
MBG, the "items" in my example is a "easy way" to give some values, In "real world" I supouse the data becomes from a service. In the subscribe of the service you can call to this.buildForm using the array of object that you receive. The only thing I want to remark is that if you has an array of object, you can mannage the FormArray itself
0

you can declare the input names as array like this:

<!-- ... -->
<div *ngFor="let selectedApi of selectedApps;let i=index">
<input type="text" [attr.name]="'obj['+i+'][users]'">
<input type="text" [attr.name]="'obj['+i+'][module]'">
<input type="text" [attr.name]="'obj['+i+'][email]'">
</div>
<!-- ... -->

So you get an array of objects.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.