如何防止表单验证中无限调用 Angular Getter 方法?

问题描述 投票:0回答:1

我目前面临着无限调用处理表单验证的 Angular 组件中的 getter 方法的问题。 getter 用于确定表单控件的有效性,但由于 Angular 的更改检测机制,它似乎会导致无限循环。

text-input.component.ts

import { CommonModule } from '@angular/common';
import { IonLabel, IonInput } from '@ionic/angular/standalone';
import { Component, inject, Input, OnDestroy, OnInit } from '@angular/core';
import { ControlContainer, FormBuilder, FormGroup, ReactiveFormsModule, Validators } from '@angular/forms';

@Component({
  standalone: true,
  selector: 'app-text-input',
  imports: [ReactiveFormsModule, IonLabel, IonInput, CommonModule],
  viewProviders: [
    {
      provide: ControlContainer,
      useFactory: () => inject(ControlContainer, { skipSelf: true }),
    },
  ],
  template: `
    <div class="input-section ion-padding-top">
      <!-- label -->
      <ion-label class="input-label">
        {{ label }}
        @if (required) {
          <span class="required-star">*</span>
        }
      </ion-label>

      <!-- input -->
      <ion-input
        mode="md"
        fill="outline"
        [ngClass]="inputClass"
        [placeholder]="placeholderText"
        [formControlName]="controlName"
      />

      <!-- validation -->
      @if (isControlInvalid) {
        <ion-label class="invalid-label">Please enter the {{ label.toLowerCase() }}.</ion-label>
      }
    </div>
  `,
})
export class TextInputComponent implements OnInit, OnDestroy {
  @Input() required = true;
  @Input() formSubmitted = false;
  @Input({ required: true }) label = '';
  @Input({ required: true }) controlName = '';
  @Input({ required: true }) placeholderText = '';

  constructor(
    private formBuilder: FormBuilder,
    private parentContainer: ControlContainer,
  ) {}

  get parentFormGroup() {
    return this.parentContainer.control as FormGroup;
  }

  // Getter to check if the control is invalid
  get isControlInvalid() {
    console.log('called 1');
    return this.formSubmitted && this.parentFormGroup.get(this.controlName)?.invalid;
  }

  // Getter to get the input class
  get inputClass() {
    console.log('called 2');
    return this.isControlInvalid ? 'invalid-input' : 'valid-input';
  }

  ngOnInit() {
    this.parentFormGroup.addControl(this.controlName, this.formBuilder.control('', Validators.required));
  }

  ngOnDestroy() {
    this.parentFormGroup.removeControl(this.controlName);
  }
}

parent.component.ts

import { Component, OnInit } from '@angular/core';
import { FormBuilder, FormGroup } from '@angular/forms';

@Component({
  selector: 'app-parent',
  templateUrl: './parent.component.html',
})
export class ParentComponent implements OnInit {
  eventForm!: FormGroup;
  formSubmitted: boolean = false;

  constructor(private fb: FormBuilder) {}

  ngOnInit() {
    // automatically add the location when call the app-text-input component
    this.eventForm = this.fb.group({});
  }
}

parent.component.html

<form [formGroup]="eventForm">
  <app-text-input 
    label="Venue"
    controlName="location" 
    [formSubmitted]="formSubmitted" 
    placeholderText="Venue Name" />
</form>

enter image description here

我可以采取什么策略来防止无限调用 getter 方法?在这种情况下,是否有以 Angular 反应形式管理状态的最佳实践可以提供帮助?

angular forms angular-reactive-forms angular-changedetection
1个回答
0
投票

每个变更检测周期都会调用 getter 方法,您可以尝试

ChangeDetectionStrategy.OnPush
减少调用次数,这将减少变更检测周期的数量。

@Component({
  standalone: true,
  selector: 'app-text-input',
  imports: [ReactiveFormsModule, IonLabel, IonInput, CommonModule],
  changeDetection: ChangeDetectionStrategy.OnPush
})
© www.soinside.com 2019 - 2024. All rights reserved.