【问题标题】:Angular 4 - nested @ViewChild is undefinedAngular 4 - 嵌套的@ViewChild 未定义
【发布时间】:2017-11-16 10:20:13
【问题描述】:

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

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

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

UserGridHeaderComponentUserGridBodyComponent 等效于 Drive-Components。

这里是html模板的相关部分:

drive-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>

drive-members-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">&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>

所以我要做的是在DriveViewComponent 中以DriveMembersModalComponent 的形式重用GridView&lt;T&gt;。当用户想要查看驱动器的成员时,它应该在弹出的引导模式中呈现类似的网格。

但是DriveViewComponentdriveMembersModal 没有完全初始化。它的

@ViewChild(UserGridComponent)
public grid: UserGridComponent;

保持未定义。似乎 Angular 在模板中找不到匹配的元素。我试过不同的选择器没有效果。我还尝试了@ViewChild(&lt;reference&gt;) 语法,其中将#reference 属性添加到元素中。然而,这给了我一个ElementRef,我不知道如何处理。它似乎只是一个 DOM 引用,而不是一个角度组件。

编辑:

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

按照这些说明重现错误:

  • 打开浏览器的调试模式以查看控制台输出
  • 点击“驱动器”标签
  • 控制台将打印DriveMembersModalComponent,它对应于上面提到的组件Cgrid 字段为 undefined。打印在DriveViewComponentngAfterViewInit()中执行

【问题讨论】:

    标签: angular angular-components viewchild


    【解决方案1】:

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

    虽然我缺少导入模块的导出语句,但 Angular 没有抛出任何错误。当您没有为模板使用显式 html 标记时,就会发生这种情况。

    您可以在简短的 plunker 之后观察到这种行为:

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

    如果您取消注释 src/module.ts 中的第 26 行,App@ViewChild 将正确呈现。仅当您将模板的 &lt;div&gt; 标记更改为 &lt;component-a&gt; 时才会引发错误。然后 Angular 会抱怨未知元素,并要求您检查它是否是 Angular 组件。

    【讨论】:

      【解决方案2】:

      在核心导入中添加 ViewChild

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

      【讨论】:

      • 你能在没有导入的情况下引用指令吗?
      【解决方案3】:

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

      我将访问我的ViewChild 的代码移至ngAfterViewInit,它就像一个魅力。

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

      【讨论】:

        猜你喜欢
        • 2018-01-24
        • 2019-06-23
        • 2019-10-19
        • 2017-01-08
        • 2018-02-25
        • 2019-07-28
        • 2020-03-25
        • 2023-01-25
        • 2017-11-25
        相关资源
        最近更新 更多