最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

Unable to update the angular dynamic reactive form controls - Stack Overflow

programmeradmin1浏览0评论

I am trying to create a dynamic angular reactive form. And able to construct a form fields with the below code, but unable to edit the form controls.

export class BillerDetailsConfirmComponent implements OnInit {
  billerForm!: FormGroup;

  constructor(
    private router: Router,
    private dialogService: NbDialogService,
    private fb: FormBuilder,
    private dashboardService: DashboardService,
    private subScriptionService: SubscriptionService,
    private cdr: ChangeDetectorRef
  ) {}

  ngOnInit(): void {
    this.initializeForm();
    this.getCompleteBillerDetails();
  }

  initializeForm(): void {
    this.billerForm = this.fb.group({
      billerDetails: this.fb.array([])
    });
  }

  getCompleteBillerDetails(): void {
    const billersSubscription = this.dashboardService.getAllBBPSBillers().subscribe(
      (respData: any) => {
        if (respData.respCode === '00') {
          console.log('API Response:', respData.response);
          this.populateFormArray(this.billerDetails, respData.response.billerDetails);
         
        }
      },
      (error: any) => {
        console.error('Error fetching biller details:', error);
      }
    );
    this.cdr.detectChanges(); 

    this.subScriptionService.add("billerCategorySubscription", billersSubscription);
  }

  populateFormArray(formArray: FormArray, data: any[]): void {
    formArray.clear();
    data.forEach(item => {
      const formGroup = this.createFormGroup(item);
      formArray.push(formGroup);
    });
    console.log('Form Array:', formArray);
  }

  createFormGroup(item: any): FormGroup {
    const formGroup: { [key: string]: FormControl } = {};

    Object.entries(item).forEach(([key, value]) => {
      if (typeof value !== 'object') {
        formGroup[key] = new FormControl(value ?? ''); // Ensure controls are created and enabled.
      }
    });

    return this.fb.group(formGroup);
  }

  get billerDetails(): FormArray {
    return this.billerForm.get('billerDetails') as FormArray;
  }


  getFormControlKeys(group: AbstractControl): string[] {
    if (group instanceof FormGroup) {
      return Object.keys(group.controls).filter(key =>
        group.get(key) instanceof FormControl
      );
    }
    return [];
  }

  getFieldChunks(keys: string[], size: number): string[][] {
    const result = [];
    for (let i = 0; i < keys.length; i += size) {
      result.push(keys.slice(i, i + size));
    }
    return result;
  }
}

View

 <nb-tab tabTitle="Biller Details">
                        <div class="block-body">
                          <form [formGroup]="billerForm">
                            <div *ngFor="let billerGroup of billerDetails.controls; let i = index" [formGroup]="billerGroup">
                              <ng-container *ngIf="billerGroup?.controls">
                                <ng-container *ngFor="let chunk of getFieldChunks(getFormControlKeys(billerGroup), 3)">
                                  <div class="row mb-4 justify-content-start">
                                    <div class="col-md-4 d-flex flex-column" *ngFor="let key of chunk">
                                      <label class="form-label">{{ key | titlecase }}</label>
                                      <input nbInput class="w-100" [formControlName]="key" placeholder="Enter {{ key }}" fieldSize="large" />
                                    </div>
                                  </div>
                                </ng-container>
                              </ng-container>
                            </div>
                          </form>
                        </div>
                      </nb-tab>

I am trying to create a dynamic angular reactive form. And able to construct a form fields with the below code, but unable to edit the form controls.

export class BillerDetailsConfirmComponent implements OnInit {
  billerForm!: FormGroup;

  constructor(
    private router: Router,
    private dialogService: NbDialogService,
    private fb: FormBuilder,
    private dashboardService: DashboardService,
    private subScriptionService: SubscriptionService,
    private cdr: ChangeDetectorRef
  ) {}

  ngOnInit(): void {
    this.initializeForm();
    this.getCompleteBillerDetails();
  }

  initializeForm(): void {
    this.billerForm = this.fb.group({
      billerDetails: this.fb.array([])
    });
  }

  getCompleteBillerDetails(): void {
    const billersSubscription = this.dashboardService.getAllBBPSBillers().subscribe(
      (respData: any) => {
        if (respData.respCode === '00') {
          console.log('API Response:', respData.response);
          this.populateFormArray(this.billerDetails, respData.response.billerDetails);
         
        }
      },
      (error: any) => {
        console.error('Error fetching biller details:', error);
      }
    );
    this.cdr.detectChanges(); 

    this.subScriptionService.add("billerCategorySubscription", billersSubscription);
  }

  populateFormArray(formArray: FormArray, data: any[]): void {
    formArray.clear();
    data.forEach(item => {
      const formGroup = this.createFormGroup(item);
      formArray.push(formGroup);
    });
    console.log('Form Array:', formArray);
  }

  createFormGroup(item: any): FormGroup {
    const formGroup: { [key: string]: FormControl } = {};

    Object.entries(item).forEach(([key, value]) => {
      if (typeof value !== 'object') {
        formGroup[key] = new FormControl(value ?? ''); // Ensure controls are created and enabled.
      }
    });

    return this.fb.group(formGroup);
  }

  get billerDetails(): FormArray {
    return this.billerForm.get('billerDetails') as FormArray;
  }


  getFormControlKeys(group: AbstractControl): string[] {
    if (group instanceof FormGroup) {
      return Object.keys(group.controls).filter(key =>
        group.get(key) instanceof FormControl
      );
    }
    return [];
  }

  getFieldChunks(keys: string[], size: number): string[][] {
    const result = [];
    for (let i = 0; i < keys.length; i += size) {
      result.push(keys.slice(i, i + size));
    }
    return result;
  }
}

View

 <nb-tab tabTitle="Biller Details">
                        <div class="block-body">
                          <form [formGroup]="billerForm">
                            <div *ngFor="let billerGroup of billerDetails.controls; let i = index" [formGroup]="billerGroup">
                              <ng-container *ngIf="billerGroup?.controls">
                                <ng-container *ngFor="let chunk of getFieldChunks(getFormControlKeys(billerGroup), 3)">
                                  <div class="row mb-4 justify-content-start">
                                    <div class="col-md-4 d-flex flex-column" *ngFor="let key of chunk">
                                      <label class="form-label">{{ key | titlecase }}</label>
                                      <input nbInput class="w-100" [formControlName]="key" placeholder="Enter {{ key }}" fieldSize="large" />
                                    </div>
                                  </div>
                                </ng-container>
                              </ng-container>
                            </div>
                          </form>
                        </div>
                      </nb-tab>
Share Improve this question asked Mar 11 at 15:52 vishnuvishnu 4,59916 gold badges62 silver badges92 bronze badges
Add a comment  | 

1 Answer 1

Reset to default 0

Let me explain:

  • I reduce the code to get better performance.
  • I hope this help you out to see how it isn't the best way to create it, and why isn't working.

The main reason as you can see with this form code is, when 1 formControlName change needs to recalculate the view again and blocking the UI for the user.

    <form [formGroup]="billerForm">
      <div formArrayName="billerDetails">
        <div *ngFor="let billerGroup of billerDetails.controls; let i = index">
          {{billerGroup.value | json}}
          <div class="row mb-4 justify-content-start">
            <ng-container [formGroupName]="i">
              <div class="col-md-4 d-flex flex-column" 
                   *ngFor="let input of billerGroup.value | keyvalue">
                  <label class="form-label">{{''+input.key|titlecase}}</label>
                  <input [formControlName]="''+input.key" />
              </div>
            </ng-container>
          </div>
        </div>
      </div>
    </form>
  • formArrayName to access array form reference.
  • billerGroup is formControl so we have access to .value
  • [formGroupName]="i" index of formArray.controls
  • | keyvalue angular pipe for get entries key,value
  • ''+input.key lifehack to give always a string.

Solution

Then I realize one thing, why angular needs to recalculate it!? and the reason is we're in a for loop and he don't know about the items!

  • One important thing is when we do *ngFor we should use trackBy, newer version using @for is required. doc trackBy

stackblitz example

<div class="col-md-4 d-flex flex-column" 
     *ngFor="let input of billerGroup.value | keyvalue; trackBy: trackBy">
 trackBy(index: number) {
    return index;
  }

now any item has reference and can update formControl!

发布评论

评论列表(0)

  1. 暂无评论