ngx-mat-select-search搜索后选中项返回初始列表第一项问题求助
ngx-mat-select-search选中项异常:点击选中后返回初始列表第一项
我配置的ngx-mat-select-search可以正常过滤选项,但点击选中某一项时,返回的却是初始列表的第一项。我参考了单选示例,但我的场景使用了FormBuilder。以下是相关代码和问题演示:

HTML代码
<mat-select [formControl]="orgaCtrl" *ngIf="ogListe" placeholder="Sélectionner un organisme" #singleSelect class="form-control organisme" formControlName="id_organisme"> <mat-option> <ngx-mat-select-search [formControl]="orgFilterCtrl" placeholderLabel="Rechercher un organisme ..." > <span ngxMatSelectNoEntriesFound> Aucun OG trouvé <button mat-button color="primary" (click)="nouvelOg()"> Ajouter votre organisme <mat-icon>add</mat-icon> </button> </span> </ngx-mat-select-search> </mat-option> <mat-option *ngFor="let og of filteredOrgs | async" [value]="og.id_organisme"> {{og.nom_organisme}} </mat-option> </mat-select>
TypeScript代码
import { AfterViewInit, Component, OnDestroy, OnInit, ViewChild } from '@angular/core'; import { FormGroup, FormBuilder, Validators, ValidatorFn, AbstractControl, FormControl } from '@angular/forms'; import { MatSelect } from '@angular/material/select'; import { Router } from '@angular/router'; import { ToastrService } from 'ngx-toastr'; import { ReplaySubject, Subject, take, takeUntil } from 'rxjs'; import { AuthService } from '../auth.service'; import { Organisme } from '../models/models'; @Component({ selector: 'app-sign-up', templateUrl: './sign-up.component.html', styleUrls: ['./sign-up.component.scss'] }) export class SignUpComponent implements OnInit, AfterViewInit, OnDestroy { form: FormGroup; appFormGroup: FormGroup; public disableSubmit = false; public formControlBuilded = false; protected organismes: Organisme[]; searchTxt: any; ogListe : boolean = true; public orgaCtrl: FormControl = new FormControl(); public orgFilterCtrl: FormControl = new FormControl(); public filteredOrgs: ReplaySubject<Organisme[]> = new ReplaySubject<Organisme[]>(1); @ViewChild('singleSelect') singleSelect: MatSelect; protected _onDestroy = new Subject<void>(); constructor( private fb: FormBuilder, private _authService: AuthService, private router: Router, private _toasterService: ToastrService ) { } ngOnInit() { this.createForm(); this._authService.getOrganismes().subscribe( res => { this.organismes = res this.filteredOrgs.next(this.organismes.slice()); this.orgFilterCtrl.valueChanges .pipe(takeUntil(this._onDestroy)) .subscribe(() => { this.filterOrgs(); }); } ); } ngAfterViewInit() { this.setInitialValue(); } ngOnDestroy() { this._onDestroy.next(); this._onDestroy.complete(); } protected setInitialValue() { this.filteredOrgs .pipe(take(1), takeUntil(this._onDestroy)) .subscribe(() => { this.singleSelect.compareWith = (a: Organisme, b: Organisme) => a && b && a.id_organisme === b.id_organisme; }); } protected filterOrgs() { if (!this.organismes) { return; } let search = this.orgFilterCtrl.value; if (!search) { this.filteredOrgs.next(this.organismes.slice()); return; } else { search = search.toLowerCase(); } this.filteredOrgs.next( this.organismes.filter(organisme => organisme.nom_organisme.toLowerCase().indexOf(search) > -1) ); } createForm() { this.form = this.fb.group({ nom_role: ['', Validators.required], prenom_role: ['', Validators.required], identifiant: ['', Validators.required], email: [ '', [Validators.email, Validators.required], ], password: ['', [Validators.required]], password_confirmation: ['', [Validators.required]], remarques: ['', Validators.required], id_organisme: ['', Validators.required], organisme: ['', Validators.required], }, { validators: [ this.ConfirmedValidator('password','password_confirmation'), this.atLeastOneRequired('id_organisme', 'organisme') ] }); this.appFormGroup = this.fb.group({ geonature_saisie: [false, null], psdrf: [false, null], ancrage: [false, null], opnl: [false, null], precisions: ['', null] }); } save() { if (this.form.valid) { this.disableSubmit = true; const finalForm = Object.assign({}, this.form.value); finalForm['champs_addi'] = this.appFormGroup.value; this._authService .signupUser(finalForm) .subscribe((res) => { this._toasterService.info('Vous recevrez un mail de confirmation quand elle aura été validée par un administrateur.','Votre demande d\'inscription a bien été prise en compte !') this.form.reset(); this.appFormGroup.reset(); window.scroll({ top: 0, left: 0, behavior: 'smooth' }); }, error => { this._toasterService.error(error.error.msg, ''); }) .add(() => { this.disableSubmit = false; }); } } ConfirmedValidator(controlName: string, matchingControlName: string) { return (formGroup: FormGroup) => { const control = formGroup.controls[controlName]; const matchingControl = formGroup.controls[matchingControlName]; if ( matchingControl.errors && !matchingControl.errors['confirmedValidator'] ) { return; } if (control.value !== matchingControl.value) { matchingControl.setErrors({ confirmedValidator: true }); } else { matchingControl.setErrors(null); } }; } atLeastOneRequired(valueName1: string, valueName2: string) { return (formgroup: FormGroup) => { const value1 = formgroup.controls[valueName1]; const value2 = formgroup.controls[valueName2]; if (value1.value || value2.value) { value1.setErrors(null); value2.setErrors(null); } else { value1.setErrors({ atLeastOneRequired: true }); value2.setErrors({ atLeastOneRequired: true }); } } } nouvelOg() { this.ogListe = false; this.form.get('id_organisme')!.patchValue(''); } }
问题原因分析
- 控件绑定冲突:mat-select同时设置了
[formControl]="orgaCtrl"和formControlName="id_organisme",双重绑定导致选中值的更新逻辑混乱,表单无法正确感知用户选择的项。 - compareWith逻辑不匹配:mat-option的
[value]绑定的是og.id_organisme(数值类型),但compareWith函数却在比较Organisme对象,类型不匹配导致无法正确匹配选中项,最终默认取过滤后列表的第一项。
解决方案
1. 移除冲突的独立控件绑定
删除mat-select上的[formControl]="orgaCtrl",直接使用表单组中的id_organisme控件:
<mat-select *ngIf="ogListe" placeholder="Sélectionner un organisme" #singleSelect class="form-control organisme" formControlName="id_organisme"> <!-- 保留其他内容不变 --> </mat-select>
2. 调整compareWith函数逻辑
将compareWith修改为比较数值类型的id值,匹配mat-option的value类型:
protected setInitialValue() { this.filteredOrgs .pipe(take(1), takeUntil(this._onDestroy)) .subscribe(() => { this.singleSelect.compareWith = (a: number, b: number) => a === b; }); }
3. 删除无用的独立控件定义
移除TS中独立的orgaCtrl变量,因为现在直接使用表单组的控件:
// 删除此行代码 // public orgaCtrl: FormControl = new FormControl();
修改完成后,点击过滤后的选项时,表单的id_organisme控件会正确获取选中项的id值,不再出现选中第一项的异常。
内容的提问来源于stack exchange,提问作者Zacharie Moulin
相关产品推荐
相关产品推荐

