You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何向组件传递委托以实现通用搜索下拉输入框?

如何向组件传递委托以实现通用搜索下拉输入框?

看起来你已经找对核心思路啦!通过让父组件传入数据加载的委托函数,让搜索下拉组件彻底和数据源解耦,这个复用性设计非常到位。我来帮你把这个组件补全,让它真正能落地使用~

首先先修正你代码里的小语法问题,@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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.17 09:33:10