Angular 4 - 嵌套的@ViewChild 未定义
Angular 4 - nested @ViewChild is undefined
在以下情况下,我无法正确初始化 @ViewChild
注释字段:
在某些情况下,@ViewChild
注释字段保持 undefined
:
X
类型的父组件实现了 XBase
类型的抽象基础 class,它具有三个抽象字段
X
类型的组件使用 A
、B
和 C
类型的 @ChildView
注释这些字段
C
反过来也扩展了 XBase
,但使用与 X
不同的模板和选择器
A
和 B
类型的字段在 X
类型的组件中得到 属性 初始化
C
类型的字段保持未定义状态
这是我的代码的简化版本
@Component({
moduleId: module.id,
selector: 'drive-view'
templateUrl: './../../html/drive-view.html'
})
export class DriveViewComponent extends GridView<Drive>
{
@ViewChild(DriveGridComponent)
public grid: DriveGridComponent;
@ViewChild(DriveDetailsComponent)
public itemDetails: DriveDetailsComponent;
@ViewChild(DriveFilterModalComponent)
public filterModal: DriveFilterModalComponent;
@ViewChild(DriveMembersModalComponent)
public driveMembersModal: DriveMembersModalComponent;
}
export abstract class GridView<TEntity>
{
public abstract grid: Grid<TEntity>;
public abstract filterModal: IGridFilterModal;
public abstract itemDetails: IGridItemDetails<TEntity>;
}
export abstract class Grid<TEntity>
{
public abstract header: IGridHeader;
public abstract body: IGridBody<TEntity>;
}
@Component({
moduleId: module.id,
selector: '[drive-grid]',
templateUrl: './../../html/grid.html'
})
export class DriveGridComponent extends Grid<Drive>
{
@ViewChild(DriveGridHeaderComponent)
public header: DriveGridHeaderComponent;
@ViewChild(DriveGridBodyComponent)
public body: DriveGridBodyComponent;
}
@Component({
moduleId: module.id,
selector: 'thead [grid-header]',
templateUrl: './../../html/drive-grid-header.html'
})
export class DriveGridHeaderComponent implements IGridHeader
{
// nothing to see here...
}
@Component({
moduleId: module.id,
selector: 'tbody [grid-body]',
templateUrl: './../../html/drive-grid-body.html'
})
export class DriveGridBodyComponent implements IGridBody<Drive>
{
// nothing to see here...
}
@Component({
moduleId: module.id,
selector: '[drive-members-modal]',
templateUrl: './../../html/drive-members-modal.html'
})
export class DriveMembersModalComponent extends GridView<User>
{
@ViewChild(UserGridComponent)
public grid: UserGridComponent;
}
@Component({
moduleId: module.id,
selector: '[user-grid]',
templateUrl: './../../html/grid.html'
})
export class UserGridComponent extends Grid<User>
{
@ViewChild(UserGridHeaderComponent)
public header: UserGridHeaderComponent;
@ViewChild(UserGridBodyComponent)
public body: UserGridBodyComponent;
}
UserGridHeaderComponent
和 UserGridBodyComponent
相当于 Drive-
组件。
这里是 html 模板的相关部分:
驱动-view.html
<div class="row">
<div class="col-lg-12"
drive-filter-modal
(onFilterApplyButtonEmitter)="onFilterApplyButton()">
</div>
</div>
<div class="row">
<div class="col-lg-12"
drive-members-modal>
</div>
</div>
<div class="row" [hidden]="!grid.body?.items">
<div class="col-lg-12"
drive-grid
(onColumnHeaderToggledEmitter)="onColumnHeaderToggled($event)"
(onDetailsButtonEmitter)="onDetailsButton($event)">
</div>
</div>
<div class="row"
id="row-grid-item-details">
<div class="col-lg-12"
[@detailsSlideUpDown]="itemDetails.fadeInOutStatus">
<item-details (onOpenModalEmitter)="onDriveMembersButton()"></item-details>
</div>
</div>
grid.html
<div class="table-responsive">
<table class="grid table table-hover">
<thead grid-header (onColumnHeaderToggledEmitter)="onColumnHeaderToggled($event)"></thead>
<tbody grid-body (onDetailsButtonEmitter)="onDetailsButton($event)"
[ngClass]="{'fadeIn': body?.items, 'fadeOut': !body?.items}"
class="animated"></tbody>
</table>
</div>
驱动器成员-modal.html
<div class="modal fade" id="drive-members-modal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel">
<div class="modal-dialog modal-lg" role="document">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
<h4 class="modal-title">Members of 'NAME'</h4>
</div>
<div class="modal-body">
<div class="row">
<div class="col-lg-12"
user-grid
(onColumnHeaderToggledEmitter)="onColumnHeaderToggled($event)"
(onDetailsButtonEmitter)="onDetailsButton($event)">
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">
<span class="glyphicon glyphicon-chevron-left"></span> Back
</button>
</div>
</div>
</div>
</div>
</div>
所以我想做的是在 DriveViewComponent
中以 DriveMembersModalComponent
的形式重用 GridView<T>
。当用户想要查看驱动器的成员时,它应该在弹出的 bootstrap 模态中呈现类似的网格。
但是 DriveViewComponent
的 driveMembersModal
没有完全初始化。它的
@ViewChild(UserGridComponent)
public grid: UserGridComponent;
保持未定义状态。似乎 angular 在模板中找不到匹配的元素。我试过不同的选择器但没有效果。我还尝试了 @ViewChild(<reference>)
语法,其中向元素添加了 #reference
属性。然而,这给了我一个 ElementRef
,我不知道如何处理。它似乎只是一个 DOM 参考,而不是 angular 组件。
编辑:
我从我的应用中创建了一个 plunker 示例:https://embed.plnkr.co/083a5AnkaiCG796adTkR/
按照这些说明重现错误:
- 打开浏览器的调试模式查看控制台输出
- 单击 "Drives" 选项卡
- 控制台会打印出
DriveMembersModalComponent
,对应上面提到的组件C
。 grid
字段是 undefined
。打印在DriveViewComponent
的ngAfterViewInit()
中执行
将上面的例子分解成一小段代码后,我找出了 @ViewChild
保持未定义的原因:
Angular 没有抛出任何错误,尽管我缺少导入模块的导出语句。当您没有为模板使用明确的 html 标记时,就会发生这种情况。
您可以在 short plunker 之后观察到此行为:
https://plnkr.co/edit/kPKSbPS86iJ1oVCA4WSj?p=preview
如果您取消注释 src/module.ts
中的第 26 行,App
的 @ViewChild
将被正确呈现。仅当您将模板的 <div>
标记更改为 <component-a>
时才会引发错误。然后 Angular 将抱怨未知元素并要求您检查它是否是 Angular 组件。
在核心导入中添加 ViewChild
import { Component, OnInit,ViewChild } from '@angular/core';
我也有类似的错误,但那只是因为我是菜鸟。事实证明,如果您尝试在组件的构造函数中访问 ViewChild
,它还不存在,并且未定义...
我将访问我的 ViewChild
的代码移到了 ngAfterViewInit
并且它非常有效。
P.S。任何带有像 ngIf/ngFor 这样的离子指令的东西似乎也不喜欢被束缚,我想这是有道理的,因为它们的存在无论如何都是依赖的。
在以下情况下,我无法正确初始化 @ViewChild
注释字段:
在某些情况下,@ViewChild
注释字段保持 undefined
:
X
类型的父组件实现了XBase
类型的抽象基础 class,它具有三个抽象字段X
类型的组件使用A
、B
和C
类型的 C
反过来也扩展了XBase
,但使用与X
不同的模板和选择器
A
和B
类型的字段在X
类型的组件中得到 属性 初始化
C
类型的字段保持未定义状态
@ChildView
注释这些字段
这是我的代码的简化版本
@Component({
moduleId: module.id,
selector: 'drive-view'
templateUrl: './../../html/drive-view.html'
})
export class DriveViewComponent extends GridView<Drive>
{
@ViewChild(DriveGridComponent)
public grid: DriveGridComponent;
@ViewChild(DriveDetailsComponent)
public itemDetails: DriveDetailsComponent;
@ViewChild(DriveFilterModalComponent)
public filterModal: DriveFilterModalComponent;
@ViewChild(DriveMembersModalComponent)
public driveMembersModal: DriveMembersModalComponent;
}
export abstract class GridView<TEntity>
{
public abstract grid: Grid<TEntity>;
public abstract filterModal: IGridFilterModal;
public abstract itemDetails: IGridItemDetails<TEntity>;
}
export abstract class Grid<TEntity>
{
public abstract header: IGridHeader;
public abstract body: IGridBody<TEntity>;
}
@Component({
moduleId: module.id,
selector: '[drive-grid]',
templateUrl: './../../html/grid.html'
})
export class DriveGridComponent extends Grid<Drive>
{
@ViewChild(DriveGridHeaderComponent)
public header: DriveGridHeaderComponent;
@ViewChild(DriveGridBodyComponent)
public body: DriveGridBodyComponent;
}
@Component({
moduleId: module.id,
selector: 'thead [grid-header]',
templateUrl: './../../html/drive-grid-header.html'
})
export class DriveGridHeaderComponent implements IGridHeader
{
// nothing to see here...
}
@Component({
moduleId: module.id,
selector: 'tbody [grid-body]',
templateUrl: './../../html/drive-grid-body.html'
})
export class DriveGridBodyComponent implements IGridBody<Drive>
{
// nothing to see here...
}
@Component({
moduleId: module.id,
selector: '[drive-members-modal]',
templateUrl: './../../html/drive-members-modal.html'
})
export class DriveMembersModalComponent extends GridView<User>
{
@ViewChild(UserGridComponent)
public grid: UserGridComponent;
}
@Component({
moduleId: module.id,
selector: '[user-grid]',
templateUrl: './../../html/grid.html'
})
export class UserGridComponent extends Grid<User>
{
@ViewChild(UserGridHeaderComponent)
public header: UserGridHeaderComponent;
@ViewChild(UserGridBodyComponent)
public body: UserGridBodyComponent;
}
UserGridHeaderComponent
和 UserGridBodyComponent
相当于 Drive-
组件。
这里是 html 模板的相关部分:
驱动-view.html
<div class="row">
<div class="col-lg-12"
drive-filter-modal
(onFilterApplyButtonEmitter)="onFilterApplyButton()">
</div>
</div>
<div class="row">
<div class="col-lg-12"
drive-members-modal>
</div>
</div>
<div class="row" [hidden]="!grid.body?.items">
<div class="col-lg-12"
drive-grid
(onColumnHeaderToggledEmitter)="onColumnHeaderToggled($event)"
(onDetailsButtonEmitter)="onDetailsButton($event)">
</div>
</div>
<div class="row"
id="row-grid-item-details">
<div class="col-lg-12"
[@detailsSlideUpDown]="itemDetails.fadeInOutStatus">
<item-details (onOpenModalEmitter)="onDriveMembersButton()"></item-details>
</div>
</div>
grid.html
<div class="table-responsive">
<table class="grid table table-hover">
<thead grid-header (onColumnHeaderToggledEmitter)="onColumnHeaderToggled($event)"></thead>
<tbody grid-body (onDetailsButtonEmitter)="onDetailsButton($event)"
[ngClass]="{'fadeIn': body?.items, 'fadeOut': !body?.items}"
class="animated"></tbody>
</table>
</div>
驱动器成员-modal.html
<div class="modal fade" id="drive-members-modal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel">
<div class="modal-dialog modal-lg" role="document">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
<h4 class="modal-title">Members of 'NAME'</h4>
</div>
<div class="modal-body">
<div class="row">
<div class="col-lg-12"
user-grid
(onColumnHeaderToggledEmitter)="onColumnHeaderToggled($event)"
(onDetailsButtonEmitter)="onDetailsButton($event)">
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">
<span class="glyphicon glyphicon-chevron-left"></span> Back
</button>
</div>
</div>
</div>
</div>
</div>
所以我想做的是在 DriveViewComponent
中以 DriveMembersModalComponent
的形式重用 GridView<T>
。当用户想要查看驱动器的成员时,它应该在弹出的 bootstrap 模态中呈现类似的网格。
但是 DriveViewComponent
的 driveMembersModal
没有完全初始化。它的
@ViewChild(UserGridComponent)
public grid: UserGridComponent;
保持未定义状态。似乎 angular 在模板中找不到匹配的元素。我试过不同的选择器但没有效果。我还尝试了 @ViewChild(<reference>)
语法,其中向元素添加了 #reference
属性。然而,这给了我一个 ElementRef
,我不知道如何处理。它似乎只是一个 DOM 参考,而不是 angular 组件。
编辑:
我从我的应用中创建了一个 plunker 示例:https://embed.plnkr.co/083a5AnkaiCG796adTkR/
按照这些说明重现错误:
- 打开浏览器的调试模式查看控制台输出
- 单击 "Drives" 选项卡
- 控制台会打印出
DriveMembersModalComponent
,对应上面提到的组件C
。grid
字段是undefined
。打印在DriveViewComponent
的ngAfterViewInit()
中执行
将上面的例子分解成一小段代码后,我找出了 @ViewChild
保持未定义的原因:
Angular 没有抛出任何错误,尽管我缺少导入模块的导出语句。当您没有为模板使用明确的 html 标记时,就会发生这种情况。
您可以在 short plunker 之后观察到此行为:
https://plnkr.co/edit/kPKSbPS86iJ1oVCA4WSj?p=preview
如果您取消注释 src/module.ts
中的第 26 行,App
的 @ViewChild
将被正确呈现。仅当您将模板的 <div>
标记更改为 <component-a>
时才会引发错误。然后 Angular 将抱怨未知元素并要求您检查它是否是 Angular 组件。
在核心导入中添加 ViewChild
import { Component, OnInit,ViewChild } from '@angular/core';
我也有类似的错误,但那只是因为我是菜鸟。事实证明,如果您尝试在组件的构造函数中访问 ViewChild
,它还不存在,并且未定义...
我将访问我的 ViewChild
的代码移到了 ngAfterViewInit
并且它非常有效。
P.S。任何带有像 ngIf/ngFor 这样的离子指令的东西似乎也不喜欢被束缚,我想这是有道理的,因为它们的存在无论如何都是依赖的。