6

Good Day.

I looked for the solution, and even though there are a view similar question to what I am asking, I am sure this one is a bit more unique and not a duplicate.

here is my html form:

<div class="form-group col-md-6" formGroupName="schema">
  <div formArrayName="currencies">
    <input type="text" class="form-control" id="percentage" formControlName="percentage" placeholder="Discount %*" required>
  </div>
</div>

and here is my ts formBuilder.

this.createPromo = this.fb.group({
      type: ['promotion'],
      name: ['', Validators.required],
      description: ['', Validators.required],
      enabled: ['', Validators.required],
      promotion_type: ['', Validators.required],
      start: ['', Validators.required],
      end: ['', Validators.required],
      schema: this.fb.group({
        currencies: this.fb.array([
          this.fb.group({
            percentage: '',
            currency: 'ZAR'
          })
        ])
      }),
    });

So I want my form to submit as a grouped array. However the error in the console is the following Cannot find control with path: 'schema -> currencies -> percentage', thus I am not able to submit my form as percentage is empty even after I input a number.

1
  • You'll need a wrapping div with formArrayName="currencies" and inside that another [formGroup]="i" where I would be the index of the array elements you loop through, and then inside that, you'll have your formControlName="percentage" and formControlName="currency" Commented Oct 16, 2018 at 8:31

1 Answer 1

13

You'll need the following for your scenario:

  1. A parent div with formGroupName="schema".
  2. Inside that, a div with formArrayName="currencies".
  3. Inside that, a div with ngFor="let currencyGroup of currencyFormGroups; let i = index;". Notice that currencyFormGroups is a getter in your Component Class.
  4. Inside that, a div with [formGroupName]="i" where i is the index that we created on the fly within *ngFor.
  5. Insde that, two inputs with formControlName="percentage" and formControlName="currency" respectively.

.

Here's all these steps translated to code:

import { Component } from '@angular/core';
import { FormGroup, FormControl, FormArray, Validators, FormBuilder } from '@angular/forms';

@Component({
  selector: 'my-app',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent {

  createPromo: FormGroup;

  constructor(private fb: FormBuilder) { }

  ngOnInit() {
    this.createPromo = this.fb.group({
      'type': ['type'],
      name: ['name', Validators.required],
      description: ['description', Validators.required],
      enabled: ['enabled', Validators.required],
      promotion_type: ['promotion_type', Validators.required],
      start: ['start', Validators.required],
      end: ['end', Validators.required],
      schema: this.fb.group({
        currencies: this.fb.array([
          this.fb.group({
            percentage: 'percentage',
            currency: 'ZAR'
          }),
          this.fb.group({
            percentage: 'percentage',
            currency: 'INR'
          }),
        ])
      }),
    });
  }

  get currencyFormGroups() {
    return (<FormArray>(<FormGroup>this.createPromo.get('schema')).get('currencies')).controls;
  }

}

Template:

<form [formGroup]="createPromo">

  ...

  <div formGroupName="schema">
    <div formArrayName="currencies">
      <div *ngFor="let currencyGroup of currencyFormGroups; let i = index;">
        <div [formGroupName]="i">
          <input 
            type="text" 
            name="percentage"
            formControlName="percentage">
          <input 
            type="text" 
            name="currency"
            formControlName="currency">
        </div>
      </div>
    </div>
  </div>

</form>

Here's a Sample StackBlitz for your ref.

PS: For simplicity's sake, I've considered all the form controls as input. Please make your changes accordingly.

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

Comments

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.