Home > Software design >  How to update value of one formControl based on the changes made in other formControl values?
How to update value of one formControl based on the changes made in other formControl values?

Time:09-16

I made a reactive form in angular with three editable formControls. The HTML code:

  <table class="table-details">
    <tr>
      <th>ID</th>
      <th>Name</th>
      <th>Carbohydrates</th>
      <th>Fats</th>
      <th>Protein</th>
      <th>Calories</th>
      <th style="padding-left: 4rem">Status</th>
      <th>Action</th>
    </tr>
    <tr *ngFor="let food of foods; index as index">
      <td>{{ food.id }}</td>
      <td>
        <editable class="name" (update)="updateField(index, 'name')">
          <ng-template appViewMode>
            {{food.name}}
            <mat-icon>edit</mat-icon>
          </ng-template>
          <ng-template appEditMode>
            <input [formControl]="getControl(index, 'name')" focusable>
          </ng-template>
        </editable>
      </td>
      <td>
        <editable class="carbohydrates" (update)="updateField(index, 'carbohydrates')">
          <ng-template appViewMode>
            {{food.carbohydrates}} gms
            <mat-icon>edit</mat-icon>
          </ng-template>
          <ng-template appEditMode>
            <input [formControl]="getControl(index, 'carbohydrates')" focusable>
          </ng-template>
        </editable>
      </td>
      <td>
        <editable class="fats" (update)="updateField(index, 'fats')">
          <ng-template appViewMode>
            {{food.fats}} gms
            <mat-icon>edit</mat-icon>
          </ng-template>
          <ng-template appEditMode>
            <input [formControl]="getControl(index, 'fats')" focusable>
          </ng-template>
        </editable>
      </td>
      <td>
        <editable class="protein" (update)="updateField(index, 'protein')">
          <ng-template appViewMode>
            {{food.protein}}gms
            <mat-icon>edit</mat-icon>
          </ng-template>
          <ng-template appEditMode>
            <input [formControl]="getControl(index, 'protein')" focusable>
          </ng-template>
        </editable>
      </td>
      <td>
        <div>
          <p [formControl]="getCalories()">
            {{food.calories}} kcals
          </p>
        </div>
      </td>
      <td>
        <div class="boolean">
          <mat-icon>lens</mat-icon>
          Active
        </div>
      </td>
      <td>
        <div class="actions">
          <mat-icon>delete</mat-icon>
        </div>
      </td>
    </tr>
  </table>

This is the ts code:

export class FoodDetailsComponent implements OnInit {

  items!: FormArray

  foods:any = FOODDATAS;

  constructor(private fb : FormBuilder) {}

  ngOnInit(): void {
    const toGroup = this.foods.map((food:any) => {
      return new FormGroup({
        name: new FormControl(food.name,[
          Validators.required,
          Validators.pattern(/^[a-zA-Z]*$/)
        ]),
        carbohydrates: new FormControl(food.carbohydrates, [
          Validators.required,
          Validators.pattern(/^\d (\.\d{1,2})?$/)
        ]),
        fats: new FormControl(food.fats, [
          Validators.required,
          Validators.pattern(/^\d (\.\d{1,2})?$/)
        ]),
        protein: new FormControl(food.protein, [
          Validators.required,
          Validators.pattern(/^\d (\.\d{1,2})?$/)
        ]),
        calories: new FormControl(food.calories, [
          Validators.required
        ])
      });
    });
    this.items = new FormArray(toGroup)

  }

  getCalories() : FormControl {
    const carbs:any = this.items.get('carbohydrates.2.inArray')
    const protein:any = this.items.get('protein.3.inArray')
    const fats:any = this.items.get('fats.2.inArray')
    return ((carbs * 4)   (protein * 4)   (fats * 9)) as unknown as FormControl
  }

  getControl(index: number, field: string): FormControl {
    return this.items.at(index).get(field) as FormControl;
  }

  updateField(index: number, field: string) {
    const control = this.getControl(index, field);
    if (control.valid) {
      this.foods = this.foods.map((e: any, i: number) => {
        if (index === i) {
          return {
            ...e,
            [field]: control.value
          };
        }
        return e;
      });
    }
  }

The values of 'calories',I want to be automatically calculated based on the values of 'carbohydrates','protein' and 'fats', using this logic

function getCalories(carbs:Double, protein:Double, fat:Double) {
   return carbs x 4   protein x 4   fats x 9
}

I want it to monitor the changes of the other three formControl values. I tried using valueChanges but it was not working with fromArray.(P.S-how to access values of fromArray?)

Thanks in advance

CodePudding user response:

ValueChanges is not working because you are not wrapping your form array directive with a parent form group directive. Also I suggest to not use this many template functions, an easier way to access controls by letting formarray handling it for you, example below

<!-- below is what you are missing, a parent form group-->
<table [formGroup]="formGroup">
  <tbody formArrayName="items">
      <tr 
      *ngFor="let item of formGroup.get('items').controls; let i = index;"> 
          <td [formGroupName]="i">
          <input formControlName="carbohydrates" placeholder="Item name">
          </td>
      </tr>
   </tbody>
</table>
  • Related