我查看了多个堆栈溢出标头。在 Array 中,我根据员工数量开发了一个动态结构。不使用 FormArray 的更实用和尖锐的答案。
FormArray 的例子令人困惑,如果有可用的数据数组,那对我来说是不必要的。
我意识到我在我的项目中更实际地使用了类似的结构。希望能给大家一点点帮助。
form.component.html
<form [formGroup]="coordinateFormGroup">
<div class="coordinate-item" *ngFor="let coordinate of coordinates; index as i" fxLayout="row"
fxLayoutAlign="start center">
<div class="circle">
{{ coordinate.label }}
</div>
<div class="source-type">
<mat-form-field>
<mat-select formControlName="sourceType{{ i + 1 }}"
placeholder="Select Soruce Type..."
required>
<mat-option *ngFor="let source of sources$ | async" [value]="source">
{{ source.title }}
</mat-option>
</mat-select>
<mat-error *ngIf="coordinateControls({
controlName: 'sourceType',
index: i,
errorName: 'required' })">
Required
</mat-error>
</mat-form-field>
</div>
<div class="add-photo">
<button mat-icon-button aria-label="take a photo">
<mat-icon>camera_alt</mat-icon>
</button>
</div>
</div>
<button mat-raised-button color="primary" (click)="onSubmit()" [disabled]="!this.coordinateFormGroup.valid">
SUBMIT
</button>
</form>
form.component.ts
import { Component, OnInit } from '@angular/core';
import { FormBuilder, FormGroup, Validators, FormControl } from '@angular/forms';
import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';
import { SourcesQuery, SourcesGQL } from '@generated-types';
interface Coordinate {
lat: number,
lng: number
}
@Component({
selector: 'app-form',
templateUrl: './form.component.html',
styleUrls: ['./form.component.scss']
})
export class FormComponent implements OnInit {
coordinateFormGroup: FormGroup;
coordinates: Coordinate[] = [];
sources$: Observable<SourcesQuery['sources']>; // for option select
constructor(
private sourcesGQL: SourcesGQL,
private formBuilder: FormBuilder,
) {
}
ngOnInit(): void {
this.getSources(); // async
// init coordinate form group
this.coordinateFormGroup = this.formBuilder.group({
// must be added for each coordinate object.
});
}
// cleaner error caller on the html side, also a shortcut to walk through the index
coordinateControls({ controlName, index, errorName }: { controlName: string, index: number, errorName: string }): boolean {
// bring errors for each different formControlName in the loop.
const name = `${controlName}${index + 1}`;
return this.coordinateFormGroup.controls[name].hasError(errorName);
}
// Data from the coordinate service.
// The data emitted are reprocessed here in real time with formBuilder.
// This function works whenever data changes.
getCoordinateCache(coordinates: Coordinate[]): Coordinate[] {
this.coordinates = coordinates;
// we rebuild the form group every time.
this.coordinateFormGroup = this.formBuilder.group({
});
// we add new formControl for each coordinate.
coordinates.forEach((obj, i) => {
this.coordinateFormGroup.addControl(`sourceType${i + 1}`, new FormControl('', [Validators.required]));
});
console.log(this.coordinateFormGroup);
return this.coordinates;
}
// Data received for option select.
getSources(): void {
this.sources$ = this.sourcesGQL
.watch({ keyword: '', skip: 0, take: 0 })
.valueChanges.pipe(map(({ data }) => {
return data.sources;
}));
}
}
截图