2

What I expect to achieve is selecting a product that comes with a price, adding the quantity, and then selecting another product still from that same list of products. The challenge is this works as expected on my local machine but when I push the changes online, it duplicates the first selected product on every addItem() function.

I can't tell what I'm missing.

This is my component:

pform: FormGroup;
iform:FormGroup;

createDataForm() {
  this.pform = this.fb.group({
    amount: [''],
    issueDate: ['', Validators.required],
    profileId: ['', Validators.required],
    items: this.fb.array([
      this.iform = this.fb.group({
      product: [{ value: {}, disabled:false}],
      quantity: [0, Validators.min(1)],
      price: [''],
      total: ['']
      })
    ])
  })
}

get items() {
  return this.pform.get('items') as FormArray;
}

addItem() {
  this.items.push(this.fb.group({
    product: [{ value: {}, disabled:false}],
    quantity: [0, Validators.min(1)],
    price: [''],
    total:['']
  }));
  this.calculateTotal();
}

removeItem(index: number): void {
  this.items.removeAt(index)
  this.calculateTotal()
}

calculateTotal() {
  let sum = 0;
  this.items.controls.forEach(control => {
    sum += control.value.total;
  });
  this.pform.patchValue({ amount:sum });
  console.log(sum)
}

// This will calculate each product Total
calItemTotal(control: FormGroup) {
  const quantity = control.value.quantity;
  const price = control.value.price;
  control.patchValue({ total: quantity * price });
  // this.calculateTotal();
}

compareFn(product: Product, _product: Product) {
  return product && _product ? product.id === _product.id : product === _product;
}

// this puts the price for every selected product
onSelectProduct(event: any, index: number) {
  const selectedProduct = event;
  this.items.at(index).get('price').setValue(selectedProduct.price);
}

This is my HTML:

<div formArrayName="items">
  <div *ngFor="let item of items.controls; let i = index">
    <hr/>
    <h5>Product {{i + 1}}</h5>
    <div  [formGroup]="iform">
      <div class="row">

        <!-- Product -->
        <div class="col-md-4">
          <div class="form-group row">
            <label class="col-sm-3 col-form-label" for="product">Product</label>
            <div class="col-sm-9">
              <ng-select
              formControlName="product"
              [compareWith]="compareFn"
              [formControl]="item?.get('product')"
              [items]="products"
              (change)="onSelectProduct($event, i)"
              bindLabel="product"
              bindValue="product">
              </ng-select>
            </div>
          </div>
        </div>

          <!-- Price -->
          <div class="col-md-1">
            <div class="form-group row">
              <!-- <label class="col-xs-3 col-form-label" for="price">P</label> -->
              <div class="col-xs-6 offset-1">
                <input type="number" class="form-control"
                id="price" placeholder="0"
                formControlName="price" [formControl]="item?.get('price')" readonly>
              </div>
            </div>
          </div>

        <!-- Qty -->
        <div class="col-md-3">
          <div class="form-group row">
            <label class="col-xs-2 offset-1 col-form-label" for="quantity">Qty</label>
            <div class="col-xs-6 offset-1">
              <input type="number" class="form-control"
              id="quantity" placeholder="0"
              formControlName="quantity" [formControl]="item?.get('quantity')" (change)="calItemTotal(item)">
            </div>
          </div>
        </div>

        <!-- Total -->
        <div class="col-md-3">
          <div class="form-group row">
            <label class="col-xs-3 col-form-label" for="total">Amt</label>
            <div class="col-xs-6 offset-1">
              <input type="number" class="form-control"
              id="total" placeholder="0"
              formControlName="total" [formControl]="item?.get('total')" readonly>
            </div>
          </div>
        </div>



        <!-- Button -->
        <div class="col-md-1">
          <div class="form-group row">
            <span (click)="removeItem(i)" class="btn btn-sm btn-warning btn-rounded btn-fw"><span><i class="icofont icofont-trash"></i></span></span>
          </div>
        </div>

      </div>
    </div>

  </div>
</div>

1 Answer 1

1
  1. You share the same iform FormGroup instance in the FormArray. Each item should have its own FormGroup instance via [formGroupName]="i" instead of [formGroup]="iform".

  2. Using [formControlName] is duplicate (functionality) with [formControl]. You should use either one but not both. And be careful when using [formControl] as you must handle the type correctly by specifying it as FormControl type otherwise you may get the compilation error when you are enabling strict mode for strict type-checking in tsconfig.json.

  3. Create a function (createItemFormGroup) for creating the item FormGroup instance so you don't need to duplicate it everywhere.

<div [formGroup]="pform">
  <div formArrayName="items">
    <div *ngFor="let item of items.controls; let i = index">
      <hr />
      <h5>Product {{i + 1}}</h5>
      <div [formGroupName]="i">
        <div class="row">
          <!-- Product -->
          <div class="col-md-4">
            <div class="form-group row">
              <label class="col-sm-3 col-form-label" for="product"
                >Product</label
              >
              <div class="col-sm-9">
                <ng-select
                  formControlName="product"
                  [compareWith]="compareFn"
                  [items]="products"
                  (change)="onSelectProduct($event, i)"
                  bindLabel="product"
                  bindValue="product"
                >
                </ng-select>
              </div>
            </div>
          </div>

          <!-- Price -->
          <div class="col-md-1">
            <div class="form-group row">
              <!-- <label class="col-xs-3 col-form-label" for="price">P</label> -->
              <div class="col-xs-6 offset-1">
                <input
                  type="number"
                  class="form-control"
                  id="price"
                  placeholder="0"
                  formControlName="price"
                  readonly
                />
              </div>
            </div>
          </div>

          <!-- Qty -->
          <div class="col-md-3">
            <div class="form-group row">
              <label class="col-xs-2 offset-1 col-form-label" for="quantity"
                >Qty</label
              >
              <div class="col-xs-6 offset-1">
                <input
                  type="number"
                  class="form-control"
                  id="quantity"
                  placeholder="0"
                  formControlName="quantity"
                  (change)="calItemTotal(item)"
                />
              </div>
            </div>
          </div>

          <!-- Total -->
          <div class="col-md-3">
            <div class="form-group row">
              <label class="col-xs-3 col-form-label" for="total">Amt</label>
              <div class="col-xs-6 offset-1">
                <input
                  type="number"
                  class="form-control"
                  id="total"
                  placeholder="0"
                  formControlName="total"
                  readonly
                />
              </div>
            </div>
          </div>

          <!-- Button -->
          <div class="col-md-1">
            <div class="form-group row">
              <span
                (click)="removeItem(i)"
                class="btn btn-sm btn-warning btn-rounded btn-fw"
                ><span><i class="icofont icofont-trash"></i></span
              ></span>
            </div>
          </div>
        </div>
      </div>
    </div>
  </div>
</div>
createDataForm() {
  this.pform = this.fb.group({
    amount: [''],
    issueDate: ['', Validators.required],
    profileId: ['', Validators.required],
    items: this.fb.array([
      this.createItemFormGroup()
    ]),
  });
}

createItemFormGroup() {
  return this.fb.group({
    product: [{ value: {}, disabled: false }],
    quantity: [0, Validators.min(1)],
    price: [''],
    total: [''],
  })
}

addItem() {
  this.items.push(
    this.createItemFormGroup()
  );
  this.calculateTotal();
}

Demo @ StackBlitz

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

1 Comment

Thanks a lot mate! The correction worked like a charm. I'm most greatful

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.