flyingpig
flyingpig

Reputation: 622

Angular Reactive Forms Array

I am having problem accesssing products array located inside opticanOrders which is inside orderForm. In the console, I see that in order to access products array, I should reference it like that:

orderForm.controls.opticianOrders.controls.products.controls

But it doesn't work.

This is my component:

  constructor(private customerService: CustomerService, private fb: FormBuilder) { }

  orderForm: FormGroup;

  ngOnInit() {
    this.orderForm = this.fb.group({
      name: [''],
      surName: [''],
      opticianOrders: this.fb.group({
        orderDescription: [''],
        products: this.fb.array([
          this.initProduct()
        ])
      }),
    });
  }

  save(model: Customer) {
    // call API to save customer
    console.log(model);
  }

  onCancel(form: NgForm){
    this.createState.emit(false);
  }

  initProduct(){
    return this.fb.group({
      name: [''],
      manufacturerName: ['']
    })
  }

  addProduct(){
    const control = <FormArray>this.orderForm.controls['products'];
    control.push(this.initProduct());
  }

  removeProduct(i: number){
    const control = <FormArray>this.orderForm.controls['products']
  }

Html

<form [formGroup]="orderForm" novalidate (ngSubmit)="save(orderForm)">

  <!-- name -->
  <div class="form-group">
      <label>Name</label>
      <input type="text" formControlName="name">
  </div>

  <!-- surName -->
  <div class="form-group">
      <label>Last Name</label>
      <input type="text" formControlName="surName">
  </div>

  <div formGroupName="opticianOrders" class="form-group">
      <label>Order Description</label>
      <input type="text" formControlName="orderDescription">
  </div>
  <div formArrayName="products">
          <div *ngFor="let product of orderForm.controls.opticianOrders.controls.products.controls; let i=index">
              <div>
                  <span>Address {{i + 1}}</span>
                  <span *ngIf="orderForm.controls.opticianOrders.controls.products.controls.length > 1" 
                      (click)="removeProduct(i)">
                  </span>
               </div>

               <div [formGroupName]="i">
                  <div>
                      <label>Product name</label>
                      <input type="text" formControlName="name">
                  </div>
              </div>
          </div>
        </div>
    <button type="submit" [disabled]="!orderForm.valid">Submit</button>
</form>

Upvotes: 1

Views: 2423

Answers (3)

zgue
zgue

Reputation: 3850

Your stackblitz code does not work. You did not import the ReactiveFormsModule and you implemented the forms code in the hello.component.ts also you put the template code in the app.component.html.

See my working sample on stackblitz. It let you add (click on Add) and remove (click on 'x') products from your FormArray.

Form value with two products:

{
  name: 'John',
  surName: 'Doe',
  opticianOrders: {
    orderDescription: '1234',
    products: [
      { name: 'Cookies', manufacturerName: '' },
      { name: 'More Cookies', manufacturerName: '' }
    ]
  }
}

For typescript this.orderForm.controls.opticianOrders is an AbstractControl which has no controls property. You have to cast it to a FormGroup first. Same with products, you have to cast it to a FormArray.

removeProduct(i: number){
  const aFormGroup = this.orderForm.controls.opticianOrders as FormGroup;
  const aFormArray = aFormGroup.controls.products as FormArray;
  aFormArray.removeAt(i);    
}

Upvotes: 0

Raj
Raj

Reputation: 638

Please replace your HTML Code as per below

<form [formGroup]="orderForm" (ngSubmit)="save()">

  <!-- name -->
  <div class="form-group">
    <label>Name</label>
    <input type="text" formControlName="name">
  </div>

  <!-- surName -->
  <div class="form-group">
    <label>Last Name</label>
    <input type="text" formControlName="surName">
  </div>

  <div class="form-group">
    <label>Order Description</label>
    <input type="text" formControlName="orderDescription">
  </div>
  <div formArrayName="products">
    <div *ngFor="let product of orderForm.controls.products['controls']; let i=index">
      <div>
        <span><strong>Product {{i + 1}}</strong></span>
        <span class="fa fa-times" *ngIf="orderForm.controls['products'].controls.length > 1" (click)="removeProduct(i)">
        </span>
      </div>

      <div [formGroupName]="i">
        <div>
          <label>Product name</label>
          <input type="text" formControlName="name">
        </div>
        <div>
          <label>Product Manufacturer name</label>
          <input type="text" formControlName="manufacturerName">
        </div>
      </div>
    </div>

    <div class="margin-20">
      <a (click)="addProduct()" style="cursor: pointer; text-transform: uppercase; font-weight: 500">
        Add another Entry +
      </a>
    </div>
  </div>
  <button class="btn btn-primary" type="submit" [disabled]="orderForm.invalid">Submit</button>
</form>

Please replace TS code as per below. I have used a trick in save form method, check whether it works for you or not?

  constructor(private fb: FormBuilder) { }

  orderForm: FormGroup;

  ngOnInit() {
    this.orderForm = this.fb.group({
      name: ['', Validators.required],
      surName: ['', Validators.required],
      orderDescription: ['', Validators.required],
      products: this.fb.array([
        this.initProduct()
      ])
    });
  }

  save() {
    console.log(this.orderForm.value);
    const obj = {
      name: this.orderForm.value.name,
      surName: this.orderForm.value.surName,
      orderDescription: this.orderForm.value.orderDescription,
      opticianOrders: {
        products: this.orderForm.value.products
      },
    };

    console.log(obj);
  }

  initProduct() {
    return this.fb.group({
      name: ['', Validators.required],
      manufacturerName: ['', Validators.required]
    })
  }

  addProduct() {
    const control = <FormArray>this.orderForm.controls['products'];
    control.push(this.initProduct());
  }

  removeProduct(i: number) {
    const control = <FormArray>this.orderForm.controls['products'];
    control.removeAt(i);
  }

Upvotes: 2

Joseph
Joseph

Reputation: 7785

Try this

orderForm.controls.opticianOrders.controls

Upvotes: 0

Related Questions