angulartypescriptangular-dynamic-forms

Generate Form Fields for each Record in Angular


I have https://stackblitz.com/edit/ingecalc?file=src/app/app.component.ts

  paramsFormArray: FormArray;  
  properties: Record<string, number> = { a: 1, b: 2 };

  constructor(private fb: FormBuilder) {
    this.paramsFormArray = this.fb.group(
      this.properties,
    );
  }

Now I want that for each property in properties to have an input in the HTML.

a = [1   ]
b = [2   ]
c = ...
d = ...

However it does not work. I tried also "array" instead of "group" does not work either...


Solution

  • There is a lot of things going wrong. So Ill give my level best to explain, please ask any doubts if you have any.

    1. Calculator needs to be a service ( basically the same as class, but is accessible through constructor, like how you did, class cannot be accessed through constructor ( through Dependency Injection)

    2. Form Group needs to be initialized and we can subscribe to an observable of formgroup called valueChanges this will trigger whenever the value gets changed.

    3. In HTML we can wrap the controls inside a form and add the attribute [formGroup]="formGroup" this will map the created form group to the controls set in HTML.

    4. we do not need ngModel or keyDown since we subscribe to the changes!

    component details

    import { Component } from '@angular/core';
    import { FormBuilder, FormGroup } from '@angular/forms';
    import { debounceTime, Subscription } from 'rxjs';
    import { CalculatorService } from './calculator.service';
    
    @Component({
      selector: 'my-app',
      templateUrl: './app.component.html',
      styleUrls: ['./app.component.css'],
    })
    export class AppComponent {
      subscription: Subscription = new Subscription();
      debounceTime = 500;
    
      paramsFormGroup: FormGroup;
      properties: any = { a: 1, b: 2, c: 0, d: 0 };
    
      constructor(private fb: FormBuilder, private calculator: CalculatorService) {
        this.paramsFormGroup = this.fb.group(this.properties);
      }
    
      ngOnInit(): void {
        this.functionToBeCalled();
        this.subscription.add(
          this.paramsFormGroup.valueChanges
            .pipe(debounceTime(this.debounceTime))
            .subscribe(() => {
              this.functionToBeCalled();
            })
        );
      }
    
      getControlLabel(type: string) {
        console.log(this.paramsFormGroup && this.paramsFormGroup.controls, type);
        if (
          this.paramsFormGroup &&
          this.paramsFormGroup.controls &&
          this.paramsFormGroup.controls[type]
        ) {
          return this.paramsFormGroup.controls[type].value;
        }
        return 0;
      }
    
      functionToBeCalled() {
        const { a, b } = this.paramsFormGroup.value;
        const newProperties = this.calculator.calculate({ a, b });
        this.paramsFormGroup.patchValue(newProperties);
      }
    
      ngOnDestroy(): void {
        this.subscription.unsubscribe();
      }
    }
    

    service details

    import { Injectable } from '@angular/core';
    import { C } from './calculator/c';
    import { D } from './calculator/d';
    
    @Injectable()
    export class CalculatorService {
      constructor() {}
    
      calculate(properties: Record<string, number>) {
        const a = properties['a'];
        const b = properties['b'];
    
        const c = C(a, b);
        const d = D(a, b);
    
        properties['c'] = c;
        properties['d'] = d;
        return properties;
      }
    }
    

    stackblitz