1

I want to create a form in Angular which could submit an array of objects.

As a result I want to get such a Json:

order = {
 selectedDays: [{
  meals: [
    {name: 'breakfast',
    selected: false},
    {name: 'dinner',
    selected: false},
    {name: 'supper',
    selected: false}
  ],
  selectedDay: "10/01/2018"
}
]}

However, I'm having some issues with finding a control. I'm receiving this error:

Error: Cannot find control with path: 'selectedDays -> 0 -> meals -> breakfast'

Here is my code:

HTML:

<div class="" [formGroup]="form">
 <button (click)="addNewDay()">Dodaj nowe zamówienie</button><br><br>
  <div class="row" formArrayName="selectedDays">
    <div *ngFor="let day of form.get('selectedDays').controls; let i=index">
      <fieldset>
        <legend><h3>Chosen day {{i+1}}: </h3></legend>
          <div [formGroupName]="i">
            <div class="col-md-3">
              <h5>Choose date</h5>
              <mat-form-field >
                  <input matInput formControlName="selectedDate" [matDatepicker]="picker">
                  <mat-datepicker-toggle matSuffix [for]="picker"></mat-datepicker-toggle>
                  <mat-datepicker #picker></mat-datepicker>
              </mat-form-field>
            </div>
            <div class="col-md-3" formArrayName="meals">
              <div><mat-checkbox formControlName="breakfast"> breakfast </mat-checkbox> </div>
              <div><mat-checkbox formControlName="dinner"> dinner </mat-checkbox> </div>
              <div><mat-checkbox formControlName="supper"> supper  </mat-checkbox> </div> 
            </div>     
          </div>
      </fieldset>
  </div>
</div>
<button click="submit()">Send order</button>

<pre>Form values: {{form.value | json}}</pre>

And typescript:

export class OrderFormComponent implements OnInit {

public form: FormGroup;
public availableMeals: string[] = [
  "breakfast", "dinner", "supper"
];

order = {
  selectedDays: [
    {
      meals: [
        {name: 'breakfast',
        selected: false},
        {name: 'dinner',
        selected: false},
        {name: 'supper',
        selected: false}
      ],
      selectedDay: "10/01/2018"
    }
  ]
}
  
constructor(private orderService: OrderService,
            private formBuilder: FormBuilder) { 
  this.form = this.formBuilder.group({
    selectedDays: this.formBuilder.array([])
  })
 
  this.setSelectedDays();
}

addNewDay() {
  let control = <FormArray>this.form.controls.selectedDays;
  control.push(
    this.formBuilder.group({
      meals: this.formBuilder.array([]),
      selectedDate: new FormControl((new Date()))
    })
  )
}

setSelectedDays() {
  let control = <FormArray>this.form.controls.selectedDays;
  this.order.selectedDays.forEach(x => {
    control.push(this.formBuilder.group({ 
      meals: this.mapToCheckboxArrayGroup(this.availableMeals),
      selectedDate: x.selectedDay
    }))
  })
}

submit() {
  this.orderService.sendOrder(this.form.value).subscribe(() => {
      console.log("Order added");
    },
    err => {
      console.log('error occurred: ' + err.message);
    }
  );
}

private mapToCheckboxArrayGroup(data: string[]): FormArray {
  return this.formBuilder.array(data.map((meal) => {
    return this.formBuilder.group({
      name: meal,
      selected: false
      });
    }));
  }
}

my Day.ts object:

export interface Day {
    meals: [
        {name: 'breakfast',
        selected: boolean},
        {name: 'dinner',
        selected: boolean},
        {name: 'supper',
        selected: boolean}
    ]
    selectedDay: string;
  }
  

Probably there is something I've missed, however I can't resolve it on my own - I'm pretty new to Angular. I will be grateful for help!

1 Answer 1

1

So after making my own version of this. There are a few things to discuss. The first is your use of formControlName in the following code

<div class="col-md-3" formArrayName="meals">
    <div><mat-checkbox formControlName="breakfast"> breakfast </mat-checkbox> </div>
    <div><mat-checkbox formControlName="dinner"> dinner </mat-checkbox> </div>
    <div><mat-checkbox formControlName="supper"> supper  </mat-checkbox> </div> 
</div>

The problem is that your controls don't have names. If you pull up the form in the console you'll notice that it goes FormGroup -> Controls -> Meals -> Controls -> 0-3. You'd have to iterate through them again and try to pull out the name from the object itself. This turns out to be really hard, at least for myself it is. Instead, you might want to assign a variable to the index of the array like you did for selectedDays and then use the iterator to access the meals property of order.selectedDays[i].

If you do that, you'd end up switching from hard-coding the various checkboxes and opt for an *ngFor that will pull the information automatically and attaching the value of the selected property in order.selectedDays[i].meals[m] to the checkbox via [(ngModel)].

Check out the StackBlitz example that I linked to, I know that it looks completely different in terms of design, but the methods I mentioned are being used. The control you'd be interested in looking at for the iteration method mentioned is the order-form control, take note that in that control I use get meals(): FormArray { return this.form.get('meals') as FormArray; } which allowed me to use [formControlName] = "i".

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

1 Comment

This solution is really helpful for me. Thank you very much!

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.