我正在尝试构建它:
┌--------------------------------------------┐
| -Page 1- -Page 2- -Page 3- |
├----------┬---------------------------------┤
| Link 1 | <router-outlet></router-outlet> |
| Link 2 | |
| Link 3 | |
| | |
| | |
左侧的链接列表取决于页面。
典型页面如下:
<div>
<app-component-1 appAnchor anchorTitle="Link 1"></app-component-1>
<app-component-2 appAnchor anchorTitle="Link 2"></app-component-2>
<app-component-3 appAnchor anchorTitle="Link 3"></app-component-3>
</div>
有一些与appAnchor
相关的指令@Input() anchorTitle: string
。我想自动捕捉它们并更新左侧菜单。
当我尝试通过router-outlet
查询这些元素时,就会出现问题。
所以我尝试了:
@ViewChildren(AnchorDirective)
viewChildren: QueryList<AnchorDirective>;
@ContentChildren(AnchorDirective)
contentChildren: QueryList<AnchorDirective>;
ngAfterContentInit(): void {
console.log(`AppComponent[l.81] contentChildren`, this.contentChildren);
}
ngAfterViewInit(): void {
console.log(`AppComponent[l.85] viewChildren`, this.viewChildren);
}
但是我总是得到一些空的QueryList
:
QueryList {dirty:false,_results:Array(0),changes:EventEmitter,length:0,last:undefined,...}
我也尝试过:
@ContentChildren(AnchorDirective, {descendants: true})
@ContentChildren(AnchorDirective, {descendants: false})
最后,我尝试在最后一刻使用以下元素查询元素:
<router-outlet (activate)="foo()"></router-outlet>
注意:我不希望子组件通过服务将数据发送到父组件,因为它会导致某些ExpressionChangedAfterItHasBeenCheckedError
和is considered as a bad practice。我真的更希望直接从父组件查询链接。
编辑:这是显示问题的stackBlitz。如果在组件内部使用@ViewChildren
,则一切正常。但是,如果从根组件使用@ViewChildren
,它将失败。
答案 0 :(得分:2)
(activate)="handleActivate($event)"
选项将不起作用,因为router-outlet
元素尚未初始化。在这种情况下,$event
确实是组件实例,但是在这里并没有帮助
ViewChildren
,ContentChildren
似乎不适用于router-outlet
。我不认为他们会这么做,但是在您的StackBlitz演示中对其进行了很好的测试
您将不得不使用服务,这是一种标准的服务方式,并且是迄今为止最灵活的。您将可以使用ChangeDetectorRef.detectChanges()解决ExpressionChangedAfterItHasBeenCheckedError
,或者更好的是,使用BehaviorSubject,然后next
的值。在您的模板中,使用async
管道进行订阅,您不会收到这些错误
答案 1 :(得分:2)
关于ContentChildren
的工作方式正在进行here的讨论,它与您的问题密切相关。
并且在this comment中对此进行了解释;
在Angular内容投影和查询中,它表示“原样的内容 写在模板中”而不是“在DOM中可见的内容”
和
这种心理模型的结果是Angular只看 模板中的子节点(而不是 呈现的DOM)。
类似于上述问题;通过router-outlet
激活的组件不会被视为ViewChildren
或ContentChildren
,它们只是DOM子级,对于View和Content查询而言,这没有任何意义。 (截至今天)无法用ViewChild
或ViewChildren
或ContentChild
或ContentChildren
我建议您解决问题的方法是结合使用activate
事件和RouterOutlet的component
属性来实现所需的行为。这样的
如下定义路由器出口;
<router-outlet #myRouterOutlet="outlet" (activate)='onActivate()'></router-outlet>
并按如下所述使用它;
@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);
})
}
这是一个工作示例,具有改进的https://stackblitz.com/edit/angular-lopyp1
输入还请注意,setTimeout
是必需的,因为onActivate
在组件生命周期尚未开始或结束的路由过程中被触发。 setTimeout
确保组件生命周期已完成以及组件和基础查询已准备就绪。