Angular 2中@ContentChildren误将宿主元素纳入子组件集合问题
I get it, that's a confusing quirk! When using @ContentChildren with the same component type (via forwardRef), sometimes Angular ends up including the host component instance itself in the QueryList, leading to unexpected lengths. Here's how to fix it:
Step 1: Filter Out the Host Instance
The simplest solution is to filter out the current component instance from the QueryList. Since QueryList is a live collection, we can handle this in the ngAfterContentInit lifecycle hook (when content children are fully initialized) and subscribe to changes to keep the filtered list up-to-date.
Here's the updated code:
import { Component, ContentChildren, forwardRef, QueryList, AfterContentInit } from '@angular/core'; @Component({ selector: 'nav-list', template: '<ng-content></ng-content>' // Ensure you have ng-content for content projection }) export class NavListComponent implements AfterContentInit { // Private QueryList holding all matches (including host) @ContentChildren(forwardRef(() => NavListComponent)) private _childNavList: QueryList<NavListComponent>; // Public filtered list excluding the host component public childNavList: QueryList<NavListComponent>; ngAfterContentInit(): void { // Apply initial filter this.updateFilteredList(); // Subscribe to changes in the original QueryList to maintain accuracy this._childNavList.changes.subscribe(() => { this.updateFilteredList(); }); } private updateFilteredList(): void { this.childNavList = this._childNavList.filter(item => item !== this); } }
Why This Works
By comparing each item in the QueryList to this (the current component instance), we explicitly exclude the host element from the filtered list. The subscription to changes ensures that if any child components are added or removed later, the filtered list stays in sync.
Alternative: Use a Directive Selector
If you want to avoid filtering entirely, you can add a unique directive to your NavListComponent and query for that instead. This makes the query more specific and prevents the host from being included accidentally:
// First create a dedicated directive import { Directive } from '@angular/core'; @Directive({ selector: 'nav-list' }) export class NavListDirective {} // Update the NavListComponent to use the directive for queries @Component({ selector: 'nav-list', template: '<ng-content></ng-content>', providers: [NavListDirective] }) export class NavListComponent { // Query for the directive, and optionally read the component instance @ContentChildren(NavListDirective, { read: NavListComponent }) childNavList: QueryList<NavListComponent>; }
This approach is more explicit and eliminates the need for filtering, as the directive targets only child components in the projected content.
内容的提问来源于stack exchange,提问作者undefined

