skip to Main Content

I am using Reactive Forms and trying to dynamically perform a math calculation once a formGroup becomes valid.

I have a btn to add more form groups by using formArray and I have another btn to do the calculation when it’s clicked. However, I want the calculation to be done and displayed once all the input fields for a form group are input and valid.

I was trying to us ngIf to check if the formGroup is valid then call a function, pass it the index number (for grabbing the necessary form controls) and then do the calculation:

HTML

<div>
   Sum: 
    <ng-container *ngIf="roundInputs.get(roundCount + '')?.valid ? calculateHandicapBtnClick(roundCount) : null">
       <span *ngFor="let total of roundTotal; let sumCount = index">
          <span *ngIf="roundCount === sumCount">
            {{total}}
          </span>
            {{total}}
          </span>
     </ng-container>
</div>

TS

  calculateHandicapBtnClick(roundNumber: number) {
    this.roundTotal = [];
    let tempArray = [];
    let eighteeenHoleScore = this.roundInputs.controls[roundNumber].get('eighteenHoleScore')?.value;
    let nineHoleScore = this.roundInputs.controls[roundNumber].get('nineHoleScore')?.value;
    let total = eighteeenHoleScore + nineHoleScore
    this.roundTotal.push(total);
    // this.roundTotal.splice(roundNumber, 0, total)
    // this.roundTotal.push(eighteeenHoleScore + nineHoleScore)

    // this.roundInputs.controls.forEach((control) => {
    //   eighteeenHoleScore = control.get('eighteenHoleScore')?.value
    //   nineHoleScore = control.get('nineHoleScore')?.value
    //   this.roundTotal.push(eighteeenHoleScore + nineHoleScore)

    // })
  }

Here is what I have for how I have the formGroups setup along with how the addRound() works:

get roundInputs(): FormArray{
    return <FormArray>this.roundForm.get('roundInputs')
  }

  ngOnInit(): void {
    this.roundForm = this.fb.group({
      roundInputs: this.fb.array([ this.buildRoundForm() ])
    })
  };

addRound() {
   this.roundInputs.push(this.buildRoundForm())
  }

I can’t seem to get the total to come together and display on the page. In the console I am seeing my logs double after the form becomes valid and when I click out of the input and then clicking elsewhere on the page. From what I have read I probably shouldn’t be calling a function there in the HTML as change detection running will cause it to run multiple times (where as if I tie it to a btn I don’t have as much of an issue). You can also see I have tried various other things that are commented out such as looping through all the form controls or trying to splice and put the total in a specific position in the array (I am using an array of values to then loop through and populate those values onto the page)

Another thing I read was to possibly create a custom pipe where it takes in and does the arithmatic and displays it on the page. I also came across maybe doing something with the ngOnInit or ngOnChanges or even possibly trying to force change detection. I haven’t really found anything that works so looking for some input.

Here is a screenshot of the app and what I am trying to attempt. You can ignore the ‘Calculate Handicap’ btn as I am trying to phase that out and replace it with the above scenario

enter image description here

2

Answers


  1. Do this in the Typescript part, subscribe to status change of the form, or a single form field, and perform desired calculations.

    // let's say you subscribe to it inside ngOnInit
    
    this.roundInputs.statusChanges
      .pipe(
        filter(status => status === 'VALID'),
        takeUntil(this.untilDestroyed$) // just to be sure, unsubscribe on destroy
      )
      .subscribe(() => {
        this.calculateHandicapBtnClick(roundNumber) // provide the value
        //call your function
      });
    
    Login or Signup to reply.
  2. you can use

    <ng-container *ngIf="roundInputs.get(roundCount + '')?.valid  &&
                            calculateHandicapBtnClick(roundCount)">
    ...
    </ng-container>
    

    always you return true in your function

    calculateHandicapBtnClick(roundCount){
      ....
      return true;
    }
    

    But it’s better, as Yogi suggest it’s better peformance use statusChanges. I imagine you have a function that return the formGroup and a getter for your array

    get array()
    {
        return this.form.get('myArrayHoles') as FormArray
    }
    setFormGroup()
    {
       const formGroup=new FormGroup({
           eighteenHoleScore:new FormControl(0),
           nineHoleScore:new FormControl(0)
       });
       const newFormIndex = this.array.length - 1;
       formGroup.statusChange.pipe(filter(res => res === 'VALID')).subscribe(res=>{
           this.calculateHandicapBtnClick(newFormIndex) //<--see how you pas the "index"
       })
       return formGroup;
    }
    

    Well if you don’t want subscribe and unsubscribe, you can to have an array of "observables"

    observablesTotal$:Observable<number>[]=[] 
    

    And fill the array when you create the form

    setFormGroup()
    {
       const formGroup=new FormGroup({
           eighteenHoleScore:new FormControl(0),
           nineHoleScore:new FormControl(0)
       });
       const newFormIndex = this.array.length - 1;
       observablesTotal$[newFormIndex] = formGroup.statusChange.pipe(
                 filter(res => res === 'VALID'),
                 map(_=>this.calculateHandicapBtnClick(newFormIndex))
       )
       return formGroup;
    }
    

    So you can use in your .html

       SUM: {{observablesTotal$[index]|async}}
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search