Angular中点击mat-select后显示mat-option内容的问题求助
问题:点击mat-select后无法显示对应的宝可梦选项内容?
我正在做一个宝可梦相关项目,当前遇到的问题是:点击对应的mat-select后,无法显示对应的mat-option内容。以下是我的现有代码:
service.ts 代码
getTrainersNames(): Observable<Array<string>>{ return this.http.get<Trainer[]>(`${this.apiUrl1}`).pipe( map((entrenadores: Trainer[]) => { return entrenadores.map((entrenador: Trainer) => entrenador.fullName); }) ); } getPokemonsOfATrainer(nombreEntrenador: string): Observable<Array<Pokemon[]>> { return this.http.get<Trainer[]>(`${this.apiUrl1}?fullName=${nombreEntrenador}`).pipe( map((entrenadores: Trainer[]) => { return entrenadores.map((entrenador: Trainer) => entrenador.pokemons); }) ); }
controller.ts 代码
// INSIDE ngOnInit this.obtainData.getTrainersNames().subscribe({ next: (nombres: string[]) => { this.trainerNames = nombres }, error: (err: Error) => console.log('Hubo un error en el observable '), complete: () => { console.log('Observer got a complete notification') }, }); this.obtainData.getPokemonsOfATrainer(this.assignTrainerName()).subscribe( { next: (pokemones: Pokemon[][]) => this.pokemons = pokemones[0], error: (err: Error) => console.log('Hubo un error en el observable '), complete: () => console.log('Observer got a complete notification'), }); // INSIDE ngAfterViewInit assignTrainerName() { // Get the selected value from the mat-select element const selectedValue = this.attacksForm.controls['trainerName'].value; // Check if the selected value is in the this.trainerNames array const selectedTrainer = this.trainerNames.find(name => name === selectedValue); console.log(selectedTrainer); // If the selected value is in the array, assign it to this.trainerName if (selectedTrainer) { this.trainerName = selectedTrainer; } return this.trainerName; }
模板代码
<!--INSIDE a form--> <mat-form-field appearance="standard"> <mat-label>Selecciona un pokemon</mat-label> <mat-select matNativeControl formControlName="pokemonName" #matSelect> <mat-option *ngFor="let pokemon of pokemons" [value]="pokemon.name"> {{pokemon.name}} </mat-option> </mat-select> </mat-form-field>
请问如何调整才能让mat-option正常显示内容?
解决方案
你的核心问题在于没有监听训练师选择的变化,并且在ngOnInit中过早调用了getPokemonsOfATrainer——此时用户还没选择任何训练师,assignTrainerName返回的是undefined或者空值,导致获取不到有效宝可梦数据。
步骤1:移除无效的初始订阅
删除ngOnInit中对getPokemonsOfATrainer的初始订阅,因为这时候没有选中的训练师,请求毫无意义。
步骤2:监听训练师控件的变化
在ngOnInit中添加对trainerName表单控件的valueChanges监听,当用户选择训练师时,再去请求对应的宝可梦列表:
// INSIDE ngOnInit this.obtainData.getTrainersNames().subscribe({ next: (nombres: string[]) => { this.trainerNames = nombres; // 可选:如果需要默认选中第一个训练师 if (nombres.length > 0) { this.attacksForm.controls['trainerName'].setValue(nombres[0]); } }, error: (err: Error) => console.log('Error al obtener nombres de entrenadores'), complete: () => console.log('Obtención de nombres completada'), }); // 监听训练师选择变化 this.attacksForm.controls['trainerName'].valueChanges.pipe( // 过滤空值或无效值 filter(selectedName => !!selectedName && this.trainerNames.includes(selectedName)), // 避免频繁请求,可选 debounceTime(300) ).subscribe(selectedName => { this.obtainData.getPokemonsOfATrainer(selectedName).subscribe({ next: (pokemones: Pokemon[][]) => { // 确保取到有效的宝可梦数组 this.pokemons = pokemones[0] || []; // 可选:清空之前选中的宝可梦 this.attacksForm.controls['pokemonName'].setValue(null); }, error: (err: Error) => console.log('Error al obtener pokemones del entrenador'), complete: () => console.log('Obtención de pokemones completada'), }); });
步骤3:移除冗余的assignTrainerName函数
现在不需要这个函数了,因为我们直接通过valueChanges获取选中的训练师名称,并且已经做了有效性校验(filter操作符)。
步骤4:优化内存泄漏问题(可选但推荐)
如果组件会被销毁,记得取消订阅valueChanges和HTTP请求,避免内存泄漏。可以使用takeUntil操作符:
// 在组件中定义一个销毁信号 private destroy$ = new Subject<void>(); // 在ngOnInit中修改监听逻辑 this.attacksForm.controls['trainerName'].valueChanges.pipe( filter(selectedName => !!selectedName && this.trainerNames.includes(selectedName)), debounceTime(300), takeUntil(this.destroy$) // 组件销毁时自动取消订阅 ).subscribe(selectedName => { this.obtainData.getPokemonsOfATrainer(selectedName).pipe( takeUntil(this.destroy$) ).subscribe({ // ... 之前的逻辑 }); }); // 在ngOnDestroy中发送销毁信号 ngOnDestroy() { this.destroy$.next(); this.destroy$.complete(); }
额外建议:使用Async管道简化代码
你也可以直接在模板中使用async管道,避免手动订阅:
- 在controller中定义一个
pokemons$Observable:
pokemons$!: Observable<Pokemon[]>; // INSIDE ngOnInit this.pokemons$ = this.attacksForm.controls['trainerName'].valueChanges.pipe( filter(selectedName => !!selectedName && this.trainerNames.includes(selectedName)), debounceTime(300), switchMap(selectedName => this.obtainData.getPokemonsOfATrainer(selectedName).pipe( map(pokemones => pokemones[0] || []) ) ) );
- 修改模板代码:
<mat-form-field appearance="standard"> <mat-label>Selecciona un pokemon</mat-label> <mat-select matNativeControl formControlName="pokemonName"> <mat-option *ngFor="let pokemon of pokemons$ | async" [value]="pokemon.name"> {{pokemon.name}} </mat-option> </mat-select> </mat-form-field>
这样可以完全省去手动订阅和取消订阅的逻辑,代码更简洁且安全。
内容的提问来源于stack exchange,提问作者Eric Díaz
相关产品推荐
相关产品推荐

