Reputation: 52867
I have three components: App, Parent, and Child:
App Component
@Component({
selector: 'app',
directives: [Parent,Child],
template: '<parent><child>hi</child><child>there</child></parent>'
})
export class AppComponent {
constructor() {
}
}
Parent Component
@Component({
selector: 'parent',
template: '<div><h1>Parent</h1><ng-content></ng-content></div>'
})
export class Parent {
@ContentChildren(Child) children: QueryList<Child>;
ngAfterContentInit() {
console.log(this.children);
}
}
Child Component
@Component({
selector: 'child',
template: '<div><h1>Child</h1></div>'
})
export class Child {
}
As you can see in the Parent component, I've tried to use @ContentChildren
to get the list of Child components, using the Child
type as a selector. However, this does not seem to work - the content children list is always undefined.
In the ngAfterContentInit()
method, I would have expected content children to be populated.
Am I missing something?
[Update]
So it turns out that the problem exists when all three components are in the same file (see console debug window where I output the content children):
If they are in separate files the problem goes away:
Normally, I would only place all the components in the same file for learning purposes. But it has me curious. Does anyone know why the behaviour is different?
Upvotes: 26
Views: 20480
Reputation: 8139
You need to add { descendants: true }
to include nested children,
@Component({
selector: 'parent',
template: '<div><h1>Parent</h1><ng-content></ng-content></div>'
})
export class Parent {
@ContentChildren(Child, { descendants: true }) children: QueryList<Child>;
ngAfterContentInit() {
console.log(this.children);
console.log(this.children.length);
}
}
descendants - True to include all descendants, otherwise include only direct children.
Upvotes: 16
Reputation: 3191
This also happens if your child component references your parent component in the constructor; Angular doesn't like the circular reference!
I needed to choose only one method for parent->child communication: either use ContentChildren
OR use the parent component in the child's constructor
.
@Component()
export class Parent {
@ContentChildren(Child)
private children: QueryList<Child>;
}
@Component()
export class Child {
constructor(
// !! Remove the following line to make the ContentChildren "work" again.
@Optional() parent: Parent
) { }
}
Upvotes: 1
Reputation: 48555
You need to use forwardRef to reference classes which are not yet defined. See this plunk. Remember ES6 classes are not hoisted.
@Component({
selector: 'parent',
template: '<div><h1>Parent</h1><ng-content></ng-content></div>'
})
export class Parent {
@ContentChildren(forwardRef(() => Child)) children; // <!- HERE
ngAfterContentInit() {
console.log(this.children);
console.log(this.children.length);
}
}
UPD Mark Rajcok pointed out an excellent article about forward references in angular2 (see the comment bellow). Must read: thoughtram.io Forward references in Angular 2.
Upvotes: 29