Angular 4 - 嵌套的@ViewChild未定义

时间:2017-06-14 10:13:54

标签: angular angular-components viewchild

我无法在以下情况下正确初始化@ViewChild注释字段:

@ViewChild带注释的字段在某些情况下会保留undefined

  • 类型X的父组件实现类型为XBase的抽象基类,它有三个抽象字段
  • X类型的组件使用@ChildView类型ABC
  • C注释这些字段
  • XBase反过来也扩展了X,但使用了与A不同的模板和选择器
  • 类型BX的字段在类型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

UserGridBodyComponentDrive-等同于<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> 组件。

以下是html模板的相关部分:

驱动-view.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>

grid.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">&times;</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>

驱动构件-modal.html

GridView<T>

因此,我尝试做的是在DriveMembersModalComponent内重用DriveViewComponent形状的DriveViewComponent。当用户想要查看驱动器的成员时,它应该在弹出的引导模式中呈现类似的网格。

driveMembersModal @ViewChild(UserGridComponent) public grid: UserGridComponent; 未完全初始化。其

@ViewChild(<reference>)

保持未定义。似乎角度无法在模板中找到匹配的元素。我尝试过不同的选择器而没有效果。我还尝试了#reference语法,其中ElementRef属性添加到元素中。然而,这给了我一个DriveMembersModalComponent,我不知道如何处理。它似乎只是一个DOM参考,但不是一个角度组件。

修改

我已经从我的应用中创建了一个plunker示例:https://embed.plnkr.co/083a5AnkaiCG796adTkR/

按照以下说明重现错误:

  • 打开浏览器的调试模式以查看控制台输出
  • 点击&#34;驱动器&#34;标签
  • 控制台将打印C,它对应于上面提到的组件gridundefined字段为DriveViewComponent。打印在ngAfterViewInit()&#39; {{1}}
  • 中执行

3 个答案:

答案 0 :(得分:3)

在将上面的示例分解为最小的代码之后,我找出了@ViewChild保持未定义的原因:

虽然我错过了导入的模块的导出语句,但是Angular没有抛出任何错误。如果您没有为模板使用明确的html标记,就会发生这种情况。

您可以在短弹射器后观察此行为:

https://plnkr.co/edit/kPKSbPS86iJ1oVCA4WSj?p=preview

如果您取消注释src/module.ts中的第26行,App的{​​{1}}将会正确呈现。如果您将模板的@ViewChild代码更改为<div>,则只会出现错误。然后Angular将抱怨未知元素并要求您检查它是否是Angular组件。

答案 1 :(得分:1)

我也有类似的错误,但这只是因为我是菜鸟。事实证明,如果您尝试在组件的构造函数中访问 ViewChild,它还不存在,并且是未定义的...

我将访问我的 ViewChild 的代码移到了 ngAfterViewInit,它的作用就像一个魅力。

附言任何像 ngIf/ngFor 这样的离子指令似乎也不喜欢被绑定,我想这是有道理的,因为它们的存在无论如何都是依赖的。

答案 2 :(得分:-1)

在核心导入中添加ViewChild

import { Component, OnInit,ViewChild } from '@angular/core';