angularangular-reactive-forms

Angular Reactive form - which input changed


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.


Solution

  • 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>