Angular组件间数据传递问题求助:实现顶部导航搜索关键词同步至卡片列表组件的过滤功能
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?
- No change emission from TopBar: Your
TopBarComponent's_searchKeywordupdates when the user types, but there's no way to send that change toCardListComponent. - Mismatched Input and Setter: You defined
@Input() _searchKeywordinCardListComponent, but your setter is forsearchKeyword—Angular won't trigger the setter when_searchKeywordchanges because they're separate properties. - One-time initialization only: The
console.log(this._searchKeyword)inCardList'sngOnInitonly 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
ngOnDestroyto avoid memory leaks. - Use
includes()instead ofindexOf() !== -1for more readable string checks. - Handle empty input to ensure all employees show when the search box is cleared.
内容的提问来源于stack exchange,提问作者j1n

