如何向组件传递委托以实现通用搜索下拉输入框?
如何向组件传递委托以实现通用搜索下拉输入框?
看起来你已经找对核心思路啦!通过让父组件传入数据加载的委托函数,让搜索下拉组件彻底和数据源解耦,这个复用性设计非常到位。我来帮你把这个组件补全,让它真正能落地使用~
首先先修正你代码里的小语法问题,@Input的类型定义应该是这样的(你之前的=>是转义字符,换成正常箭头函数即可):
@Input() loadValues!: (searchTerm: string) => Observable<string[]>;
接下来我们把组件的逻辑补全,加入防抖、值绑定、下拉状态管理这些关键功能:
import { Component, Input } from '@angular/core'; import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'; import { Observable, Subject } from 'rxjs'; import { debounceTime, switchMap, tap } from 'rxjs/operators'; @Component({ selector: 'search-dropdown', templateUrl: './search-dropdown.component.html', styleUrls: ['./search-dropdown.component.scss'], providers: [ { provide: NG_VALUE_ACCESSOR, multi: true, useExisting: SearchDropdownComponent } ] }) export class SearchDropdownComponent implements ControlValueAccessor { @Input() loadValues!: (searchTerm: string) => Observable<string[]>; // 输入框当前值 searchTerm = ''; // 下拉选项列表 options: string[] = []; // 下拉框显示状态 showDropdown = false; // 用于防抖的Subject private searchSubject = new Subject<string>(); // ControlValueAccessor 所需的回调 onChange: (value: string) => void = () => {}; onTouched: () => void = () => {}; constructor() { // 初始化防抖订阅 this.searchSubject.pipe( debounceTime(300), // 300ms防抖,避免频繁调用API switchMap(term => { if (term.length < 3) { // 输入字符小于3时清空选项 this.options = []; return []; } return this.loadValues(term); }), tap(options => { this.options = options; this.showDropdown = options.length > 0; }) ).subscribe(); } // 输入框值变化时触发 onInputChange(value: string): void { this.searchTerm = value; this.searchSubject.next(value); this.onTouched(); } // 选中下拉选项 selectOption(option: string): void { this.searchTerm = option; this.onChange(option); this.showDropdown = false; this.onTouched(); } // 输入框失去焦点时隐藏下拉 onBlur(): void { setTimeout(() => this.showDropdown = false, 100); // 延迟一点,避免点击选项时直接隐藏 } // ControlValueAccessor 实现:写入值到组件 writeValue(value: string): void { this.searchTerm = value || ''; } // ControlValueAccessor 实现:注册值变化回调 registerOnChange(fn: (value: string) => void): void { this.onChange = fn; } // ControlValueAccessor 实现:注册触摸回调 registerOnTouched(fn: () => void): void { this.onTouched = fn; } }
然后是对应的模板文件search-dropdown.component.html,实现输入框和下拉列表的交互:
<div class="search-dropdown-container"> <input type="text" [(ngModel)]="searchTerm" (input)="onInputChange(searchTerm)" (blur)="onBlur()" placeholder="输入至少3个字符搜索..." class="search-input" /> <ul *ngIf="showDropdown" class="dropdown-list"> <li *ngFor="let option of options" (click)="selectOption(option)" class="dropdown-item" > {{ option }} </li> </ul> </div>
最后是父组件的使用示例,你只需要在父组件里定义自己的数据源获取逻辑,然后把这个函数传给子组件即可:
// 父组件.ts文件 import { Component } from '@angular/core'; import { Observable, of } from 'rxjs'; @Component({ selector: 'app-parent', templateUrl: './parent.component.html' }) export class ParentComponent { selectedItem = ''; // 模拟API请求,实际项目里替换成真实的HTTP请求 loadSearchOptions(searchTerm: string): Observable<string[]> { // 这里可以调用你的API,比如this.http.get<string[]>(`/api/search?term=${searchTerm}`) const mockData = ['Apple', 'Banana', 'Apricot', 'Blueberry', 'Cherry']; return of(mockData.filter(item => item.toLowerCase().includes(searchTerm.toLowerCase()))); } }
父组件模板:
<!-- 父组件.html文件 --> <search-dropdown [loadValues]="loadSearchOptions" [(ngModel)]="selectedItem" ></search-dropdown> <p>你选中的内容是:{{ selectedItem }}</p>
这样你的组件就完全通用了——不管是搜索用户、商品还是其他数据,只要父组件传入对应的loadValues函数,组件就能自动处理搜索和下拉逻辑,完全不用关心数据源的细节~
备注:内容来源于stack exchange,提问作者Andy
相关产品推荐
相关产品推荐

