我正在尝试通过创建一个表单组件来实现 Angular Material Stepper,该组件可以作为多个较大表单的步骤插入。
例如,假设我有N个表单,并且第一步总是相同的(名称和地址信息),我如何将组件插入到父组件中?
我见过其他问题,但是,它们似乎都以某种方式让 Angular 抱怨,即当 strict: true 时。
我尝试了各种方法,有些方法可以通过添加空断言运算符来开始工作,但这让我觉得我没有正确输入内容或以正确的方式进行操作。
这是我尝试过的一个例子。
// insured-form.component.ts
import { Component, OnInit } from '@angular/core';
import { FormControl, FormGroup, ReactiveFormsModule } from '@angular/forms';
import { MatInputModule } from '@angular/material/input';
import { MatFormFieldModule } from '@angular/material/form-field';
import { MatButtonModule } from '@angular/material/button';
import { MatIconModule } from '@angular/material/icon';
import { MatSelectModule } from '@angular/material/select';
interface AddressForm {
line1: FormControl<string>;
line2: FormControl<string | null>;
city: FormControl<string>;
postalCode: FormControl<string>;
stateId: FormControl<string>;
}
export interface InsuredForm {
name: FormControl<string>;
dba: FormControl<string | null>;
mailingAddress: FormGroup<AddressForm>;
}
@Component({
selector: 'grid-insured-form',
standalone: true,
imports: [
ReactiveFormsModule,
MatInputModule,
MatFormFieldModule,
MatButtonModule,
MatIconModule,
MatSelectModule,
],
template: `<form [formGroup]="insuredForm" class="insured-form">
<mat-form-field class="insured-form__input">
<mat-label>Name</mat-label>
<input type="text" matInput formControlName="name" />
</mat-form-field>
<mat-form-field class="insured-form__input">
<mat-label>DBA</mat-label>
<input type="text" matInput formControlName="dba" />
</mat-form-field>
<div formGroupName="mailingAddress" class="address-form">
<h2>Mailing Address</h2>
<mat-form-field class="address-form__input">
<mat-label>Line 1</mat-label>
<input type="text" matInput formControlName="line1" />
</mat-form-field>
<mat-form-field class="address-form__input">
<mat-label>Line 2</mat-label>
<input type="text" matInput formControlName="line2" />
</mat-form-field>
<mat-form-field class="address-form__input">
<mat-label>Postal Code</mat-label>
<input type="text" matInput formControlName="postalCode" />
</mat-form-field>
<mat-form-field class="address-form__input">
<mat-label>City</mat-label>
<input type="text" matInput formControlName="city" />
</mat-form-field>
<mat-form-field class="address-form__input">
<mat-label>State</mat-label>
<mat-select formControlName="stateId">
<mat-option value="1">FL</mat-option>
</mat-select>
</mat-form-field>
</div>
</form>
`,
styleUrls: ['./insured-form.component.scss'],
})
export class InsuredFormComponent implements OnInit {
insuredForm!: FormGroup;
ngOnInit(): void {
this.insuredForm = new FormGroup<InsuredForm>({
name: new FormControl('', { nonNullable: true }),
dba: new FormControl('', { nonNullable: true }),
mailingAddress: new FormGroup<AddressForm>({
line1: new FormControl('', { nonNullable: true }),
line2: new FormControl(''),
city: new FormControl('', { nonNullable: true }),
postalCode: new FormControl('', { nonNullable: true }),
stateId: new FormControl('', { nonNullable: true }),
}),
});
}
}
// End of insured-form.component.ts
// feature-form.component.ts
import { AfterViewInit, Component, ViewChild } from '@angular/core';
import { MatStepperModule } from '@angular/material/stepper';
import { InsuredFormComponent } from '../shared/insured-form/insured-form.component';
import { FormGroup } from '@angular/forms';
@Component({
selector: 'grid-feature-form',
standalone: true,
imports: [MatStepperModule, InsuredFormComponent],
template: `<mat-stepper orientation="horizontal" #stepper>
<mat-step [stepControl]="insuredForm">
<ng-template matStepLabel>Step 1</ng-template>
<grid-insured-form></grid-insured-form>
</mat-step>
</mat-stepper>
`,
styleUrls: ['./feature-form.component.scss'],
})
export class FeatureFormComponent implements AfterViewInit {
@ViewChild(InsuredFormComponent) insuredFormComponent!: InsuredFormComponent;
insuredForm!: FormGroup;
ngAfterViewInit(): void {
this.insuredForm = this.insuredFormComponent.insuredForm;
}
}
// End of feature-form.component
以下有效,但我在控制台中出现以下错误:
Error: NG0100: ExpressionChangedAfterItHasBeenCheckedError: Expression has changed after it was checked. Previous value: 'undefined'. Current value: '[object Object]'
替代解决方案:我通过将 insuredForm 创建移动到服务中、将 Input() 添加到 insured-form 组件并在 feature-form 组件中创建表单来使其工作。但是,我真的很想将 insuredForm 封装在 insured-form 组件中。我相信这是可行的,因为 insuredForm(表单组)将在创建时在 NgOnInit 中的功能表单组件中实例化。
我也知道我可以手动运行更改检测,但 still 似乎不是正确的方法。
这不是一个小型的副项目,所以我想确保我以尽可能最好的方式做事。
注意:我正在使用 Angular 16,所以我考虑过使用 Signals,但还没有太多使用它们。
如何创建一个可重用的表单组件,可以插入到 mat-step 中,并避免当 strict 设置为 true 时编译器在控制台中发出任何抱怨?
您可以使用
static: true
选项确保查询结果在 ngOnInit 中可用。
它将允许您使用
ngOnInit
而不是 ngAfterViewInit
@ViewChild(InsuredForm, { static: true }) insuredFormComponent!: InsuredForm;
insuredForm!: FormGroup;
ngOnInit(): void {
this.insuredForm = this.insuredFormComponent.insuredForm;
}