0

I have a reactive form for entering sales that has multiple identical lines (item#, description, quantity, retail, and extended retail). It is using a FormArray if that makes any difference on the answer. If I add (change)="onChangeItem($event) to the itemNumber field, the function is fired as expected. However, I can't figure out which itemNumber field was changed (i.e. which line). The only solution I have come up with is to append the index to the id of each field so the ids will be like itemNumber1, itemNumber2, etc. but that seems like an odd way to do it. Is there something in the event that tell me which item was changed or is there another way to determine this? My goal is to not process every line every time one line changes.

1 Answer 1

1

You can use this approach:

Here is the typescript file code.

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

@Component({
  selector: 'app-sales-form',
  templateUrl: './sales-form.component.html',
  styleUrls: ['./sales-form.component.css']
})
export class SalesFormComponent implements OnInit {

  form: FormGroup;

  constructor(private fb: FormBuilder) { }

  ngOnInit(): void {
    this.form = this.fb.group({
      items: this.fb.array([this.createItem()])
    });

    // Subscribe to valueChanges on the FormArray itself
    this.items.valueChanges.subscribe((values: any[]) => {
      console.log('FormArray value changed:', values);
      // Process only the modified item
    });
  }

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

  // Create a new FormGroup for each item
  createItem(): FormGroup {
    return this.fb.group({
      itemNumber: ['', Validators.required],
      description: ['', Validators.required],
      quantity: [1, Validators.required],
      retail: [0, Validators.required],
      extendedRetail: [0, Validators.required]
    });
  }

  // Add a new item to the array
  addItem(): void {
    this.items.push(this.createItem());
  }

  // Remove an item from the array
  removeItem(index: number): void {
    this.items.removeAt(index);
  }

  // Handle individual field changes
  onChangeItem(event: any, index: number, field: string): void {
    console.log(`Item at index ${index} changed in field ${field}:`, event);
    // Handle item change here (you can optimize based on the field)
  }

  // Handle form submission
  onSubmit(): void {
    console.log(this.form.value);
  }
}

Here is the html file code.

<form [formGroup]="form" (ngSubmit)="onSubmit()">
  <div formArrayName="items">
    <div *ngFor="let item of items.controls; let i = index" [formGroupName]="i">
      
      <!-- Item Number -->
      <label for="itemNumber{{i}}">Item #</label>
      <input 
        id="itemNumber{{i}}" 
        formControlName="itemNumber"
        (change)="onChangeItem($event, i, 'itemNumber')"
      />
      
      <!-- Description -->
      <label for="description{{i}}">Description</label>
      <input 
        id="description{{i}}" 
        formControlName="description"
        (change)="onChangeItem($event, i, 'description')"
      />

      <!-- Quantity -->
      <label for="quantity{{i}}">Quantity</label>
      <input 
        id="quantity{{i}}" 
        formControlName="quantity"
        (change)="onChangeItem($event, i, 'quantity')"
      />

      <!-- Retail Price -->
      <label for="retail{{i}}">Retail</label>
      <input 
        id="retail{{i}}" 
        formControlName="retail"
        (change)="onChangeItem($event, i, 'retail')"
      />

      <!-- Extended Retail -->
      <label for="extendedRetail{{i}}">Extended Retail</label>
      <input 
        id="extendedRetail{{i}}" 
        formControlName="extendedRetail"
        (change)="onChangeItem($event, i, 'extendedRetail')"
      />

      <button type="button" (click)="removeItem(i)">Remove Item</button>
    </div>
  </div>

  <button type="button" (click)="addItem()">Add Item</button>
  <button type="submit" [disabled]="form.invalid">Submit</button>
</form>
Sign up to request clarification or add additional context in comments.

4 Comments

I never thought about adding additional parms. Great idea However, why are you subscribing to valueChanges in ngOnInit? This is not needed when using (change), correct? I just want to make sure that I am not overlooking something.
You're absolutely right! The valueChanges subscription gives you the entire object of the FormArray whenever any field changes, while onChangeItem will only give you the value of the specific input that was modified. If you're only interested in tracking individual field changes, you can safely remove the valueChanges subscription. However, if you ever need the entire object for processing, it could still be useful. 😊
Thanks for the answer and a special thanks for including very detailed code. I wanted to post my code with my question but it was so messed up, I wasn't sure that I knew what I was doing with it. I ended up deleting most of it and started over using your sample.
You're very welcome! I'm glad the code was helpful to you.

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.