Angular Mat Autocomplete funktioniert nicht in FormArray?
Ich versuche, die automatische Vervollständigung von Matten in FormArray zu implementieren. Ich bin diesem Link gefolgt, aber meine Konsole hat viele Fehler, wenn ich versuche, den Link zu öffnen, wenn ich diesen Code wie im folgenden Beispiel eingebe:
https://stackblitz.com/edit/angular-szxkme-yfphur?file=app%2Fautocomplete-display-example.ts
create-project.ts
export class CreateProjectComponent implements OnInit {
pageNavigation: IPageNavigation = {
pageTitle: 'Project Management',
subPages: [{ label: 'Projects', link: '/projects' }],
};
createProjectForm: FormGroup;
projectTeamMemberFormArray = new FormArray([]);
filteredEmployees: Observable<IEmployee[]>;
constructor(
private formBuilder: FormBuilder,
private projectManagementService: ProjectManagementService
) { }
ngOnInit(): void {
this.buildForm();
}
buildForm(): void {
this.createProjectForm = this.formBuilder.group({
projectTeamMembers: this.projectTeamMemberFormArray
});
}
manageNameControl(index: number) {
var arrayControl = this.createProjectForm.get('projectTeamMembers') as FormArray;
this.filteredEmployees[index] = arrayControl.at(index).get('firstName').valueChanges
.pipe(
startWith<string | IEmployee>(''),
map(value => typeof value === 'string' ? value : value.firstName),
map(name => name ? this.projectManagementService.filterEmployees(name) : null)
);
}
addProjectTeamMember(teamMember?: IProjectMember): void {
this.projectTeamMemberFormArray = this.createProjectForm.get('projectTeamMembers') as FormArray;
this.projectTeamMemberFormArray.push(this.createProjectTeamMemberItem(teamMember));
this.manageNameControl(this.projectTeamMemberFormArray.length - 1);
}
createProjectTeamMemberItem(teamMember?: IProjectMember): FormGroup {
return this.formBuilder.group({
employeeId: new FormControl((teamMember && teamMember.employeeId) || null, Validators.required),
firstName: new FormControl((teamMember && teamMember.firstName) || null, Validators.required),
memberRoles: new FormControl((teamMember && teamMember.projectRoles) || null, Validators.required),
});
}
removeProjectTeamMemberItem(index: number, item: IProjectMember) {
this.projectTeamMemberFormArray = this.createProjectForm.get('projectTeamMembers') as FormArray;
this.projectTeamMemberFormArray.removeAt(index);
this.projectTeamMembers.forEach((c: IProjectMember) => {
if (c.id === item.id) c.deleted = true;
});
}
selectEmployee(employee: IEmployee): void {
this.createProjectForm.get('employeeId').setValue(employee.id);
}
employeeTypeaheadDisplayFn(employee: IEmployee): string {
if (employee) {
return `${employee.lastName} ${employee.firstName}`;
}
return '';
}
}
create-project.html
<form #f="ngForm" (ngSubmit)="submit()" novalidate [formGroup]="createProjectForm">
<section>
<div formArrayName="projectTeamMembers" class="single-item-container"
*ngFor="let t of createProjectForm.get('projectTeamMembers').controls; let j = index">
<ng-container [formGroupName]="j">
<div class="spinner" *ngIf="showLoader">
<mat-progress-spinner [color]="'primary'" diameter="100" mode="indeterminate" value="50">
</mat-progress-spinner>
</div>
<div [formGroupName]="j" *ngIf="!showLoader" fxLayout="row" fxLayoutGap="2%">
<!-- employee -->
<mat-form-field fxFlex="100%">
<input matInput formControlName='firstName' placeholder="Start typing employee first or last name."
[matAutocomplete]="auto">
<mat-error *ngIf="t.get('firstName').errors?.required">
{{ 'FIELD_REQUIRED_MESSAGE' | translate }}
</mat-error>
</mat-form-field>
<mat-autocomplete #auto="matAutocomplete" [displayWith]="employeeTypeaheadDisplayFn" (optionSelected)="selectEmployee($event.option.value.id)">
<mat-option *ngFor="let employee of filteredEmployees[j] | async" [value]="employee">
{{ employee.firstName }} {{ employee.lastName }}
</mat-option>
</mat-autocomplete>
<div class="input-bottom">
<button mat-raised-button color="warn" (click)="removeProjectTeamMemberItem(j, t.value)">
Delete
</button>
</div>
</div>
</ng-container>
</div>
<div class="form-footer">
<div class="items-buttons">
<button mat-raised-button color="primary" type="button"
(click)="addProjectTeamMember()">{{'COMPONENTS.PROJECT.ADD_NEW_TEAM_MEMBER' | translate}}</button>
</div>
</div>
</section>
</form>
project-management.service.ts
export interface IEmployee {
id: number;
firstName: string;
lastName: string;
}
filterEmployees(searchTerm: string): Observable<IEmployee[]> {
const reqUrl = `${environment.mPortalWebApi}employee-profiles/search?searchTerm=${searchTerm}`;
return this.http.get(reqUrl).pipe(
map((response: IEmployee[]) => response),
catchError((error) => _throw(error)),
);
}
Ich würde es begrüßen, wenn jemand den Code anschauen und sehen würde, was ich vermisst habe. Vielen Dank.
BEARBEITEN
Nach Ihren Vorschlägen @polyglot sind einige Fehler verschwunden, nur dort gibt es ein Problem mit der asynchronen Pipe und ngFor für dieses Array, wie in der folgenden Abbildung dargestellt, und es gibt einen Fehler bei manageNameControl für this.filteredEmployees [index].
Der Typ 'Observable <Observable <IEmployee [] >>' kann nicht dem Typ 'Observable' zugewiesen werden. Beim Typ 'Observable <IEmployee []>' fehlen die folgenden Eigenschaften vom Typ 'IEmployee': id, firstName,
Antworten
Zuerst haben Sie nicht initialisiert filteredEmployees. Sollte sein:
filteredEmployees: Observable<IEmployee>[] = []; // Not Observable<IEmployee[]>
Als Nächstes haben Sie [formGroupName]="j"zweimal in create-project.html verwendet. Delete - <ng-container [formGroupName]="j">Wrapper. Und du solltest damit beginnen [formGroup]="createProjectForm".
<section [formGroup]="createProjectForm">
<div formArrayName="projectTeamMembers" class="single-item-container"
*ngFor="let t of createProjectForm.get('projectTeamMembers').controls; let j = index">
<div class="spinner" *ngIf="showLoader">
<mat-progress-spinner [color]="'primary'" diameter="100" mode="indeterminate" value="50">
</mat-progress-spinner>
</div>
<div [formGroupName]="j" *ngIf="!showLoader" fxLayout="row" fxLayoutGap="2%">
<!-- employee -->
<mat-form-field fxFlex="100%">
<input matInput formControlName='firstName' placeholder="Start typing employee first or last name."
[matAutocomplete]="auto">
<mat-error *ngIf="t.get('firstName').errors?.required">
{{ 'FIELD_REQUIRED_MESSAGE' | translate }}
</mat-error>
</mat-form-field>
<mat-autocomplete #auto="matAutocomplete" [displayWith]="employeeTypeaheadDisplayFn" (optionSelected)="selectEmployee($event.option.value.id)">
<mat-option *ngFor="let employee of filteredEmployees[j] | async" [value]="employee">
{{ employee.firstName }} {{ employee.lastName }}
</mat-option>
</mat-autocomplete>
<div class="input-bottom">
<button mat-raised-button color="warn" (click)="removeProjectTeamMemberItem(j, t.value)">
Delete
</button>
</div>
</div>
</div>
<div class="form-footer">
<div class="items-buttons">
<button mat-raised-button color="primary" type="button"
(click)="addProjectTeamMember()">{{'COMPONENTS.PROJECT.ADD_NEW_TEAM_MEMBER' | translate}}</button>
</div>
</div>
</section>
BEHOBEN
Ich habe das Problem behoben, das gefiltert sein muss
filteredEmployees: IEmployee [] und die Methode manageNameControl sehen aus
manageNameControl(index: number) {
var arrayControl = this.createProjectForm.get('projectTeamMembers') as FormArray;
arrayControl.at(index).get('employeeId').valueChanges
.pipe(
debounceTime(300),
switchMap((value) => this.projectManagementService.filterEmployees(value)),
)
.subscribe((employees: IEmployee[]) => (this.filteredEmployees = employees));
}
und aync Pipes mit aus HTML-Form entfernt
<mat-form-field fxFlex="100%">
<input matInput formControlName='employeeId' name='employeeId' placeholder="Start typing employee first or last name."
[matAutocomplete]="auto">
<mat-error *ngIf="t.get('employeeId').errors?.required">
{{ 'FIELD_REQUIRED_MESSAGE' | translate }}
</mat-error>
</mat-form-field>
<mat-autocomplete #auto="matAutocomplete" [displayWith]="employeeTypeaheadDisplayFn(filteredEmployees)">
<mat-option *ngFor="let employee of filteredEmployees" [value]="employee.id">
{{ employee.firstName }} {{ employee.lastName }}
</mat-option>
</mat-autocomplete>