Use @ViewChildren/@ContentChildren in combination with router-outlet
There is an on ongoing discussion here about how ContentChildren
works, and it is closely related to your problem.
And in this comment it is explained that:
In Angular content projection and queries it means "content as it was written in a template" and not "content as visible in the DOM"
and
The consequence of this mental model is that Angular only looks at the nodes that are children in a template (and not children in the rendered DOM).
Similar to the issue above, components activated via router-outlet
are not considered as ViewChildren
or ContentChildren
. They are just DOM children, and that doesn't mean anything to angular in terms of View and Content queries. There is no way (as of today) that they an be queried with ViewChild
or ViewChildren
or ContentChild
or ContentChildren
My suggestion to solve your issue is to use a combination of activate
event and the component
property of RouterOutlet to achieve the desired behavior. As such:
Define router-outlet as follows:
<router-outlet #myRouterOutlet="outlet" (activate)='onActivate()'></router-outlet>
And use it as follows:
@ViewChild("myRouterOutlet", { static: true }) routerOutlet: RouterOutlet;
nbViewChildren: number;
links = [];
onActivate(): void {
setTimeout(() => {
const ancList: QueryList<AnchorDirective> = (this.routerOutlet.component as any).children;
this.nbViewChildren = ancList.length;
this.links = ancList.map(anc => anc.anchorTitle);
})
}
Here is a working demo with improved typing: https://stackblitz.com/edit/angular-lopyp1
Also note that setTimeout
is required because onActivate
is fired during routing where the component life cycle hasn't started or finished yet. setTimeout
ensures that the component lifecycle has completed, and that the component and underlying queries are ready as well.
The (activate)="handleActivate($event)"
option won't work, as the router-outlet
elements won't have been initialized yet. The $event
in that case is indeed the component instance, but it's not really helpful here
The ViewChildren
, ContentChildren
don't seem to work for router-outlet
. I didn't think they would, but tested it a good bit with your StackBlitz demo
You'll have to use a service, which is the standard way of doing it, and by far the most flexible. You will be able to get around the ExpressionChangedAfterItHasBeenCheckedError
with ChangeDetectorRef.detectChanges(), or better still, use a BehaviorSubject, and next
the values from that. In your template subscribe using async
pipe, and you won't get those errors