问题

在模板中显示相应的元素后获得@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有一个选项,但它会导致太多无用的调用。

答案(设置)


当前回答

我自己在使用Angular 10时也遇到了同样的问题。

如果我尝试使用[hidden]或*ngIf,那么@ViewChild变量总是未定义的。

<p-calendar #calendar *ngIf="bShowCalendar" >
</p-calendar>

我没有把它从网页上删除。 我使用了一个[ngClass]使控件的透明度为:0,并将它完全移开。

<style>
  .notVisible {
    opacity: 0;
    left: -1000px;
    position: absolute !important;
  }
</style>

<p-calendar #calendar [ngClass]="{'notVisible': bShowCalendar }" >
</p-calendar>

我知道,这很蠢很丑,但它解决了问题。

我还必须让控制变成静态的。我不明白为什么…但是,再一次,它拒绝在没有改变的情况下工作:

export class DatePickerCellRenderer {
    @ViewChild('calendar', {static: true }) calendar: Calendar;

其他回答

上面的答案并不适用于我,因为在我的项目中,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需要一秒来分配。否则就没有定义了。

如果我在Angular 9中使用ChangeDetectorRef,它就可以工作

@ViewChild('search', {static: false})
public searchElementRef: ElementRef;

constructor(private changeDetector: ChangeDetectorRef) {}

//then call this when this.display = true;
show() {
   this.display = true;
   this.changeDetector.detectChanges();
}

简化版,我在使用谷歌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;
 }

确保将参数{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()
     }
  }
}

我认为使用从lodash延迟有很多意义,特别是在我的情况下,我的@ViewChild()是在async管道