How to get index of changed item in angular form array

Viewed 40190

I'm using Angular 4 with reactive forms. I have a form array that I am trying to tie to an array I keep track of in my component. I'm using reactive forms so I can have the validation, so I don't want to use the template forms approach.

I add items to the form array like so:

createFormWithModel() {
  this.orderForm = this.fb.group({
    orderNumber: [this.order.ProductBookingOrder],
    orderDate: [this.order.PurchaseOrderIssuedDate],
    lineDetailsArray: this.fb.array([])
  })

  const arrayControl = <FormArray>this.orderForm.controls['lineDetailsArray'];
  this.order.ProductBookingDetails.forEach(item => {
    let newGroup = this.fb.group({
      ProductName: [item.ProductName],
      Quantity: [item.ProductQuantity.Quantity],
      UOM: [item.ProductQuantity.UOM],
      RequestedShipDate: [item.RequestedShipDate]
    })
  })
}

The orderForm is obviously my reactive forms FormGroup. the order is my object I get from my API and I want to update its values, including the line details. I think I should use 'valueChanges.subscribe' on each newGroup but I'm not sure how to get the index of the item that was changed. Any thoughts?

     newGroup.valueChanges.subscribe('i want value and index some how' => {
this.order.ProductbookingDetails[index].ProductName = value.ProductName;
    });

Here's the HTML for this portion:

<tbody formArrayName="lineDetailsArray">
        <tr [formGroupName]="i" *ngFor="let line of orderForm.controls['lineDetailsArray'].controls; index as i">
          <td><input class="form-control" type="text" placeholder="Product Name" formControlName="ProductName" required/></td>
          <td><input class="form-control" type="number" step=".01" (focus)="$event.target.select()" placeholder="Quantity" formControlName="Quantity"/></td>
          <td><input class="form-control" readonly formControlName="UOM"></td>
          <td><date-picker formControlName="RequestedShipDate" format="L" [showClearButton]="false"></date-picker></td>
          <td><button type="button" (click)="deleteLineDetail(i)">Remove</button></td>
        </tr>
      </tbody>
4 Answers

I found a method to help who wants to find the element that changes the value

Here is my html

<mat-form-field>
   <mat-label>Material</mat-label>
   <mat-select formControlName="material" (ngModelChange)="materialChange($event)">
      <mat-option *ngFor="let objetct of materiais" [value]="objetct">   
        {{objetct?.nome}}
      </mat-option>
  </mat-select>
</mat-form-field>

This is my Angular ts file

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

  public form: FormGroup;
  private object: SolicitacaoMaterialDTO;
  public materiais: MaterialDTO[] = [];

  @Output() mudouValor: EventEmitter<Unidade> = new EventEmitter();

  constructor() {
    this.form = new FormGroup({
      "itens": this.formBuilder.array([])
    });
  }

  ngOnInit() {
    this.getMateriais();
 }

  getMateriais() {
    this.materialService.findAll().subscribe((res) => {
      if (res) {
        this.materiais = res;
      }
    })
  }

  // this is the method that make me find the item that changed
  materialChange($event) {
    (<FormArray>this.form.get('itens')).controls.map((material) => {
      if (material.value.material == $event) {
        material.patchValue({
          unidadeMedida:$event.unidadeMedida
        });
      }
    });
  }

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

  clickAddItem() {
    this.itens.push(this.createItem());
  }

  createItem(): FormGroup {
    return this.formBuilder.group({
      material: this.formBuilder.control(null, [Validators.required]),
      unidadeMedida: this.formBuilder.control(null),
      quantidadeSolicitada: this.formBuilder.control(null, [Validators.required]),
    });
  }
}

You can create an own component for the items that you want to maintain in your form array. For every update, delete etc. you emit an event to the parent component and the index you can either emit with the event that you bind on in the parent component or you have the index in the parent component in the template too to use it as additional input parameter for the function that you use to bind on the change event of the child component.

Read through this article and you will understand what I mean:

https://medium.com/spektrakel-blog/angular2-building-nested-reactive-forms-7978ecd145e4

Related