FormArray total of each formgroup via RxJS Stackblitz - https://stackblitz.com/edit/angular-plp4rb-formarray-total-prs9ay

I'm new to RxJS observables.

I've created a form array where the total for a line is quantity * rate. Via valueChanges for the line form group I set the total via the calcTotal method. 

On form changes I emit all the lines up to parent to calculate the total.

Questions:


Do I have any memory leaks?
Why is it if I stick debounceTime(1) inside the lineItem valueChanges then this stops working where the calculation is out by the last change?


    lineItem.valueChanges.pipe(
      takeUntil(this.destroyed$),
      debounceTime(1)
    ).subscribe(value =&gt; this.calcTotal(lineItem))



Why is it if I remove the {emitEvent: false} and put in a debounceTime as above the total stops working altogether?
Should i be using some other way like combineLatest? If so how do I collect the valueChanges observables (I'll figure it out from here if this is required)?


import { Component, OnInit, Input, Output, EventEmitter, ViewChild } from '@angular/core';
import { FormArray, FormBuilder, FormControl, FormGroup } from '@angular/forms'
import { Subject } from 'rxjs'
import { takeUntil, debounceTime} from 'rxjs/operators'
import { MatTable } from '@angular/material';

import { Line } from '../app.component';

@Component({
  selector: 'app-total-calculator',
  templateUrl: './total-calculator.component.html',
  styleUrls: ['./total-calculator.component.css']
})
export class TotalCalculatorComponent implements OnInit {
  displayedColumns = ['quantity', 'rate', 'total'];
  formGroup: FormGroup;

  @Input() lines: Line[];
  @Output() linesChange = new EventEmitter&lt;Line[]&gt;();
  @ViewChild(MatTable) private matTable: MatTable&lt;any&gt;;

  get linesArray(): FormArray {
   return this.formGroup.get('lines') as FormArray;
  }

  private destroyed$ = new Subject&lt;void&gt;();

  constructor(private fb: FormBuilder) {
   this.buildForm();
  }

  ngOnDestroy() {
   this.destroyed$.next();
   this.destroyed$.complete();
  }

  addLineClick() {
    this.addLineFormGroup();
  }

  addLineFormGroup() {
    this.linesArray.push(this
      .getLineFormGroup());
    this.matTable.renderRows();
  }

  private buildForm() {
     // formarray
     this.formGroup = this.fb.group({
       lines: this.fb.array([])
     });

     // subscribe to any changes and emit these
     this.formGroup.valueChanges.pipe(
       takeUntil(this.destroyed$),
       debounceTime(300)
     ).subscribe(value =&gt; {
        if (!this.formGroup.valid) {
          return;
        }
        this.linesChange.emit(value['lines']);
    });
  }

  getLineFormGroup(): FormGroup {
    let lineItem = this.fb.group({
      quantity: new FormControl(),
      rate: new FormControl(),
      total: new FormControl()
    });
    lineItem.valueChanges.pipe(
      takeUntil(this.destroyed$),
    ).subscribe(value =&gt; this.calcTotal(lineItem))
    return lineItem
  }

  calcTotal(line: FormGroup) {
    const quantity =  +line.controls['quantity'].value;
    const rate =      +line.controls['rate'].value;  
    line.controls['total'].setValue((quantity * rate).toFixed(2), {emitEvent: false});
  }



  ngOnInit() {
  }

}
```
Do I have any memory leaks?


No



Why is it if I stick debounceTime(1) inside the lineItem valueChanges then this stops working where the calculation is out by the last change?



  Beacuse debounceTime(1) delays the response by 1ms inside the lineItem
  and this.linesChange.emit(value['lines']);  is already run with old
  total


The implementation below will sort your problems.

  getLineFormGroup(): FormGroup {
    let lineItem = this.fb.group({
      quantity: new FormControl(),
      rate: new FormControl(),
      total: new FormControl()
    });
    lineItem.valueChanges.pipe(
      takeUntil(this.destroyed$),
      debounceTime(20)
    ).subscribe(value =&gt; 
      { 
        console.log("should be called first")
        this.calcTotal(lineItem)

        if (!this.formGroup.valid) {
          return;
        }
        this.linesChange.emit(this.formGroup.value['lines']);
      }
    )
    return lineItem
}



Why is it if I remove the {emitEvent: false} and put in a debounceTime as above the total stops working altogether?



  The lineItem.valueChanges observable will fire in a circular fashion
  when you remove this {emitEvent: false}.
  line.controls['total'].setValue() will fire lineItem.valueChanges
  again. Which in-turn crashes the app and stop.



Should i be using some other way like combineLatest? If so how do I collect the valueChanges observables (I'll figure it out from here if this is required)?



  I would suggest not required