Angular 2 @ViewChild注解返回undefined
我正在学习Angular 2。
我想使用@ViewChild注解从父组件访问一个子组件。
这里有几行代码:
在BodyContent.ts中我有:
import {ViewChild, Component, Injectable} from 'angular2/core'; import {FilterTiles} from '../Components/FilterTiles/FilterTiles'; @Component({ selector: 'ico-body-content' , templateUrl: 'App/Pages/Filters/BodyContent/BodyContent.html' , directives: [FilterTiles] }) export class BodyContent { @ViewChild(FilterTiles) ft:FilterTiles; public onClickSidebar(clickedElement: string) { console.log(this.ft); var startingFilter = { title: 'cognomi', values: [ 'griffin' , 'simpson' ]} this.ft.tiles.push(startingFilter); } }
而在FilterTiles.ts中 :
import {Component} from 'angular2/core'; @Component({ selector: 'ico-filter-tiles' ,templateUrl: 'App/Pages/Filters/Components/FilterTiles/FilterTiles.html' }) export class FilterTiles { public tiles = []; public constructor(){}; }
最后在这里的模板(如评论中所build议的):
BodyContent.html
<div (click)="onClickSidebar()" class="row" style="height:200px; background-color:red;"> <ico-filter-tiles></ico-filter-tiles> </div>
FilterTiles.html
<h1>Tiles loaded</h1> <div *ngFor="#tile of tiles" class="col-md-4"> ... stuff ... </div>
FilterTiles.html模板被正确地加载到ico-filter-tiles标记(的确能看到标题)。
注意:BodyContent类使用DynamicComponetLoader注入到另一个模板(Body)中:dcl.loadAsRoot(BodyContent,'#ico-bodyContent',injector):
import {ViewChild, Component, DynamicComponentLoader, Injector} from 'angular2/core'; import {Body} from '../../Layout/Dashboard/Body/Body'; import {BodyContent} from './BodyContent/BodyContent'; @Component({ selector: 'filters' , templateUrl: 'App/Pages/Filters/Filters.html' , directives: [Body, Sidebar, Navbar] }) export class Filters { constructor(dcl: DynamicComponentLoader, injector: Injector) { dcl.loadAsRoot(BodyContent, '#ico-bodyContent', injector); dcl.loadAsRoot(SidebarContent, '#ico-sidebarContent', injector); } }
问题是,当我尝试将ft写入控制台日志时,出现“未定义” ,当然,当我尝试在“tiles”数组内部推送某些内容(没有属性tile为“undefined”)时,我会遇到exception。
还有一件事:FilterTiles组件似乎正确加载,因为我能够看到它的HTML模板。
任何build议? 谢谢
我有类似的问题,并认为我会张贴,以防其他人犯了同样的错误。 首先,要考虑的一件事是AfterViewInit; 你需要等待视图被初始化,然后才能访问你的@ViewChild。 不过,我的@ViewChild仍然返回null。 问题是我的* ngIf。 * ngIf指令正在杀死我的控件组件,所以我不能引用它。
import {Component, ViewChild, OnInit, AfterViewInit} from 'angular2/core'; import {ControlsComponent} from './controls/controls.component'; import {SlideshowComponent} from './slideshow/slideshow.component'; @Component({ selector: 'app', template: ` <controls *ngIf="controlsOn"></controls> <slideshow (mousemove)="onMouseMove()"></slideshow> `, directives: [SlideshowComponent, ControlsComponent] }) export class AppComponent { @ViewChild(ControlsComponent) controls:ControlsComponent; controlsOn:boolean = false; ngOnInit() { console.log('on init', this.controls); // this returns undefined } ngAfterViewInit() { console.log('on after view init', this.controls); // this returns null } onMouseMove(event) { this.controls.show(); // throws an error because controls is null } }
希望有所帮助。
前面提到的问题是ngIf导致视图未定义。 答案是使用ViewChildren而不是ViewChild。 我有类似的问题,我不希望一个网格显示,直到所有的参考数据已被加载。
HTML:
<section class="well" *ngIf="LookupData != null"> <h4 class="ra-well-title">Results</h4> <kendo-grid #searchGrid> </kendo-grid> </section>
组件代码
import { Component, ViewChildren, OnInit, AfterViewInit, QueryList } from '@angular/core'; import { GridComponent } from '@progress/kendo-angular-grid'; export class SearchComponent implements OnInit, AfterViewInit { //other code emitted for clarity @ViewChildren("searchGrid") public Grids: QueryList<GridComponent> private SearchGrid: GridComponent public ngAfterViewInit(): void { this.Grids.changes.subscribe((comps: QueryList <GridComponent>) => { this.SearchGrid = comps.first; }); } }
在这里,我们使用ViewChildren,您可以在其中侦听更改。 在这种情况下,任何孩子参考#searchGrid。 希望这可以帮助。
这对我有效。
例如,我的名为“my-component”的组件使用* ngIf =“showMe”显示,如下所示:
<my-component [showMe]="showMe" *ngIf="showMe"></my-component>
所以,当组件被初始化时,组件还没有被显示,直到“showMe”为真。 因此,我的@ViewChild引用都是未定义的。
这是我使用@ViewChildren和它返回的QueryList。 查看有关QueryList和@ViewChildren使用演示的文章 。
您可以使用@ViewChildren返回的QueryList,并使用rxjs订阅引用项目的任何更改,如下所示。 @ViewChid没有这个能力。
import { Component, ViewChildren, ElementRef, OnChanges, QueryList, Input } from '@angular/core'; import 'rxjs/Rx'; @Component({ selector: 'my-component', templateUrl: './my-component.component.html', styleUrls: ['./my-component.component.css'] }) export class MyComponent implements OnChanges { @ViewChildren('ref') ref: QueryList<any>; // this reference is just pointing to a template reference variable in the component html file (ie <div #ref></div> ) @Input() showMe; // this is passed into my component from the parent as a ngOnChanges () { // ngOnChanges is a component LifeCycle Hook that should run the following code when there is a change to the components view (like when the child elements appear in the DOM for example) if(showMe) // this if statement checks to see if the component has appeared becuase ngOnChanges may fire for other reasons this.ref.changes.subscribe( // subscribe to any changes to the ref which should change from undefined to an actual value once showMe is switched to true (which triggers *ngIf to show the component) (result) => { // console.log(result.first['_results'][0].nativeElement); console.log(result.first.nativeElement); // Do Stuff with referenced element here... } ); // end subscribe } // end if } // end onChanges } // end Class
希望这有助于节省一些时间和挫折。
我的解决方法是使用[style.display] =“getControlsOnStyleDisplay()”,而不是* ngIf =“controlsOn”。 该块在那里,但不显示。
@Component({ selector: 'app', template: ` <controls [style.display]="getControlsOnStyleDisplay()"></controls> ... export class AppComponent { @ViewChild(ControlsComponent) controls:ControlsComponent; controlsOn:boolean = false; getControlsOnStyleDisplay() { if(this.controlsOn) { return "block"; } else { return "none"; } } ....
它必须工作。
但是正如GünterZöchbauer所说的那样,模板中一定还有其他的问题。 我创造了一个相关的普朗克答案 。 请检查浏览器的控制台。
boot.ts
@Component({ selector: 'my-app' , template: `<div> <h1> BodyContent </h1></div> <filter></filter> <button (click)="onClickSidebar()">Click Me</button> ` , directives: [FilterTiles] }) export class BodyContent { @ViewChild(FilterTiles) ft:FilterTiles; public onClickSidebar() { console.log(this.ft); this.ft.tiles.push("entered"); } }
filterTiles.ts
@Component({ selector: 'filter', template: '<div> <h4>Filter tiles </h4></div>' }) export class FilterTiles { public tiles = []; public constructor(){}; }
它像一个魅力。 请仔细检查您的标签和参考。
谢谢…
我的解决scheme是将ngIf从子组件的外部移动到包含整个html部分的div上的子组件的内部。 这样,当它需要的时候它仍然隐藏起来,但是能够加载组件,我可以在父项中引用它。
这适用于我,看下面的例子。
import {Component, ViewChild, ElementRef} from 'angular2/core'; @Component({ selector: 'app', template: ` <a (click)="toggle($event)">Toggle</a> <div *ngIf="visible"> <input #control name="value" [(ngModel)]="value" type="text" /> </div> `, }) export class AppComponent { private elementRef: ElementRef; @ViewChild('control') set controlElRef(elementRef: ElementRef) { this.elementRef = elementRef; } visible:boolean; toggle($event: Event) { this.visible = !this.visible; if(this.visible) { setTimeout(() => { this.elementRef.nativeElement.focus(); }); } } }
这将是容易的。 试试这个,如果你不想渲染元素,仍然通过没有任何错误。 只要放置一个if条件来检查undefined:
@ViewChild(ControlsComponent) controls:ControlsComponent; ..... if(controls){ controls.yourChildFunction(); }