问题
在模板中显示相应的元素后获得@ViewChild的最优雅的方式是什么?
下面是一个例子。也可提供活塞。
Component.template.html:
<div id="layout" *ngIf="display">
<div #contentPlaceholder></div>
</div>
Component.component.ts:
export class AppComponent {
display = false;
@ViewChild('contentPlaceholder', { read: ViewContainerRef }) viewContainerRef;
show() {
this.display = true;
console.log(this.viewContainerRef); // undefined
setTimeout(() => {
console.log(this.viewContainerRef); // OK
}, 1);
}
}
我有一个默认隐藏其内容的组件。当有人调用show()方法时,它变得可见。然而,在Angular 2变更检测完成之前,我不能引用viewContainerRef。如上所示,我通常将所有必需的操作包装到setTimeout(()=>{},1)中。有没有更正确的方法?
我知道ngAfterViewChecked有一个选项,但它会导致太多无用的调用。
答案(设置)
为ViewChild使用setter:
private contentPlaceholder: ElementRef;
@ViewChild('contentPlaceholder') set content(content: ElementRef) {
if(content) { // initially setter gets called with undefined
this.contentPlaceholder = content;
}
}
一旦*ngIf变成true, setter就会被元素引用调用。
注意,对于Angular 8,你必须确保设置{static: false},这是其他Angular版本的默认设置:
@ViewChild('contentPlaceholder', { static: false })
注意:如果contentPlaceholder是一个组件,你可以改变ElementRef到你的组件类:
private contentPlaceholder: MyCustomComponent;
@ViewChild('contentPlaceholder') set content(content: MyCustomComponent) {
if(content) { // initially setter gets called with undefined
this.contentPlaceholder = content;
}
}
确保将参数{static: false}传递给@ViewChild可以解决这个问题。
template.html代码
<div *ngIf="showFirtChild">
<first-child #firstchildComponent ></first-child>
</div>
在.ts文件
export class Parent implements {
private firstChild: FirstchildComponent;
@ViewChild('firstchildComponent', { static: false }) set content(content:
FirstchildComponent) {
if(content) {
this.firstchildComponent = content;
}
}
ShowChild(){
this.showFirtChild = true;
if(this.firstchildComponent){
this.firstchildComponent.YourMethod()
}
}
}
上面的答案并不适用于我,因为在我的项目中,ngIf是在一个输入元素上。我需要访问nativeElement属性,以便在ngIf为真时专注于输入。在ViewContainerRef上似乎没有nativeElement属性。以下是我所做的(遵循@ViewChild文档):
<button (click)='showAsset()'>Add Asset</button>
<div *ngIf='showAssetInput'>
<input #assetInput />
</div>
...
private assetInputElRef:ElementRef;
@ViewChild('assetInput') set assetInput(elRef: ElementRef) {
this.assetInputElRef = elRef;
}
...
showAsset() {
this.showAssetInput = true;
setTimeout(() => { this.assetInputElRef.nativeElement.focus(); });
}
我在聚焦之前使用了setTimeout,因为ViewChild需要一秒来分配。否则就没有定义了。
简化版,我在使用谷歌Maps JS SDK时遇到了类似的问题。
我的解决方案是将divand ViewChild提取到它自己的子组件中,当在父组件中使用时,可以使用*ngIf隐藏/显示。
之前
HomePageComponent模板
<div *ngIf="showMap">
<div #map id="map" class="map-container"></div>
</div>
HomePageComponent组件
@ViewChild('map') public mapElement: ElementRef;
public ionViewDidLoad() {
this.loadMap();
});
private loadMap() {
const latLng = new google.maps.LatLng(-1234, 4567);
const mapOptions = {
center: latLng,
zoom: 15,
mapTypeId: google.maps.MapTypeId.ROADMAP,
};
this.map = new google.maps.Map(this.mapElement.nativeElement, mapOptions);
}
public toggleMap() {
this.showMap = !this.showMap;
}
后
MapComponent模板
<div>
<div #map id="map" class="map-container"></div>
</div>
MapComponent组件
@ViewChild('map') public mapElement: ElementRef;
public ngOnInit() {
this.loadMap();
});
private loadMap() {
const latLng = new google.maps.LatLng(-1234, 4567);
const mapOptions = {
center: latLng,
zoom: 15,
mapTypeId: google.maps.MapTypeId.ROADMAP,
};
this.map = new google.maps.Map(this.mapElement.nativeElement, mapOptions);
}
HomePageComponent模板
<map *ngIf="showMap"></map>
HomePageComponent组件
public toggleMap() {
this.showMap = !this.showMap;
}