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

Angular组件间数据传递问题求助:实现顶部导航搜索关键词同步至卡片列表组件的过滤功能

Fixing Angular Component Data Sharing Between TopBar and CardList

Hey there! Let's break down why your search keyword isn't getting passed from top-bar to card-list, and fix it with two common Angular approaches tailored to your setup.

What's Wrong With Your Current Code?

  1. No change emission from TopBar: Your TopBarComponent's _searchKeyword updates when the user types, but there's no way to send that change to CardListComponent.
  2. Mismatched Input and Setter: You defined @Input() _searchKeyword in CardListComponent, but your setter is for searchKeyword—Angular won't trigger the setter when _searchKeyword changes because they're separate properties.
  3. One-time initialization only: The console.log(this._searchKeyword) in CardList's ngOnInit only runs once (on component load), so it won't show updates as the user types.

Solution 1: Use @Output + Parent Component Mediator

This works best if both components are children of the same parent component.

Step 1: Update TopBarComponent to Emit Keyword Changes

Add an @Output to broadcast search keyword updates:

@Component({
 selector: 'top-bar',
 templateUrl: './top-bar.component.html',
 styleUrls: ['./top-bar.component.scss']
})
export class TopBarComponent implements OnInit {
 _searchKeyword: string = '';
 @Output() searchKeywordUpdated = new EventEmitter<string>(); // Add this

 constructor(
  public employeeService: EmployeeService,
  private activatedRoute: ActivatedRoute,
 ) { }
 ngOnInit(): void { }
}

Update the TopBar HTML to emit changes on user input:

<div id="top-search" class="right-container w-1/4">
 <input 
  class="w-full px-2 py-1 rounded-md" 
  type="text" 
  placeholder="Search" 
  [(ngModel)]="_searchKeyword"
  (ngModelChange)="searchKeywordUpdated.emit($event)" <!-- Add this -->
 />
</div>

Step 2: Connect Components via Parent Component

In your parent component's template, link the top-bar output to a local variable, then pass that variable to card-list:

<!-- Parent component template -->
<top-bar (searchKeywordUpdated)="onSearchKeywordChange($event)"></top-bar>
<card-list [searchKeyword]="currentSearchKeyword"></card-list>

In the parent component's TypeScript:

export class ParentComponent {
 currentSearchKeyword: string = '';

 onSearchKeywordChange(keyword: string) {
  this.currentSearchKeyword = keyword;
 }
}

Step 3: Fix CardListComponent's Input and Setter

Align the @Input with your setter to trigger filtering when the keyword changes:

@Component({
 selector: 'card-list',
 templateUrl: './card-list.component.html',
 styleUrls: ['./card-list.component.scss']
})
export class CardListComponent implements OnInit {
 employees: Employee[] | any[] = [];
 filteredEmployees: Employee[] | any[] = [];
 private _searchKeyword: string = '';

 @Input() 
 set searchKeyword(value: string) { // Bind @Input directly to the setter
  this._searchKeyword = value;
  this.filteredEmployees = this.filterEmployees(value);
 }
 get searchKeyword(): string {
  return this._searchKeyword;
 }

 constructor(
  private employeeService: EmployeeService,
  private activatedRoute: ActivatedRoute,
  private ref: ChangeDetectorRef) { }

 ngOnInit(): void {
  this.employees = this.activatedRoute.snapshot.data['employee'];
  this.filteredEmployees = this.employees;
 }

 filterEmployees(userInput: string): any {
  if (!userInput) return this.employees; // Show all employees if input is empty
  return this.employees.filter(em => 
    em.firstname.toLowerCase().includes(userInput.toLowerCase())
  );
 }
}

Solution 2: Use a Shared Service (For Independent/Non-Sibling Components)

If your top-bar and card-list don't share a direct parent, a shared service with observables is a cleaner, scalable approach.

Step 1: Create a Shared Search Service

import { Injectable } from '@angular/core';
import { BehaviorSubject, Observable, Subscription } from 'rxjs';

@Injectable({ providedIn: 'root' }) // Makes the service available app-wide
export class SearchService {
 private searchKeywordSubject = new BehaviorSubject<string>('');
 public searchKeyword$: Observable<string> = this.searchKeywordSubject.asObservable();

 updateSearchKeyword(keyword: string): void {
  this.searchKeywordSubject.next(keyword);
 }
}

Step 2: Update TopBarComponent to Use the Service

Inject the service and call updateSearchKeyword when the user types:

@Component({
 selector: 'top-bar',
 templateUrl: './top-bar.component.html',
 styleUrls: ['./top-bar.component.scss']
})
export class TopBarComponent implements OnInit {
 _searchKeyword: string = '';

 constructor(
  public employeeService: EmployeeService,
  private activatedRoute: ActivatedRoute,
  private searchService: SearchService // Inject the service
 ) { }
 ngOnInit(): void { }
}

Update the HTML:

<div id="top-search" class="right-container w-1/4">
 <input 
  class="w-full px-2 py-1 rounded-md" 
  type="text" 
  placeholder="Search" 
  [(ngModel)]="_searchKeyword"
  (ngModelChange)="searchService.updateSearchKeyword($event)"
 />
</div>

Step 3: Update CardListComponent to Subscribe to the Service

Inject the service and listen for keyword changes:

@Component({
 selector: 'card-list',
 templateUrl: './card-list.component.html',
 styleUrls: ['./card-list.component.scss']
})
export class CardListComponent implements OnInit, OnDestroy {
 employees: Employee[] | any[] = [];
 filteredEmployees: Employee[] | any[] = [];
 private keywordSubscription!: Subscription; // Track subscription for cleanup

 constructor(
  private employeeService: EmployeeService,
  private activatedRoute: ActivatedRoute,
  private ref: ChangeDetectorRef,
  private searchService: SearchService // Inject the service
 ) { }

 ngOnInit(): void {
  this.employees = this.activatedRoute.snapshot.data['employee'];
  this.filteredEmployees = this.employees;

  // Subscribe to keyword updates
  this.keywordSubscription = this.searchService.searchKeyword$.subscribe(keyword => {
    this.filteredEmployees = this.filterEmployees(keyword);
  });
 }

 ngOnDestroy(): void {
  // Clean up subscription to prevent memory leaks
  if (this.keywordSubscription) this.keywordSubscription.unsubscribe();
 }

 filterEmployees(userInput: string): any {
  if (!userInput) return this.employees;
  return this.employees.filter(em => 
    em.firstname.toLowerCase().includes(userInput.toLowerCase())
  );
 }
}

Quick Tips

  • Always clean up subscriptions in ngOnDestroy to avoid memory leaks.
  • Use includes() instead of indexOf() !== -1 for more readable string checks.
  • Handle empty input to ensure all employees show when the search box is cleared.

内容的提问来源于stack exchange,提问作者j1n

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.28 10:42:27