问题

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

答案(设置)


当前回答

在我的例子中,我只需要在模板中存在div时加载整个模块,这意味着出口在ngif中。这样,每当angular检测到#geolocalisationOutlet元素时,它就会在其中创建一个组件。该模块也只加载一次。

constructor(
    public wlService: WhitelabelService,
    public lmService: LeftMenuService,
    private loader: NgModuleFactoryLoader,
    private injector: Injector
) {
}

@ViewChild('geolocalisationOutlet', {read: ViewContainerRef}) set geolocalisation(geolocalisationOutlet: ViewContainerRef) {
    const path = 'src/app/components/engine/sections/geolocalisation/geolocalisation.module#GeolocalisationModule';
    this.loader.load(path).then((moduleFactory: NgModuleFactory<any>) => {
        const moduleRef = moduleFactory.create(this.injector);
        const compFactory = moduleRef.componentFactoryResolver
            .resolveComponentFactory(GeolocalisationComponent);
        if (geolocalisationOutlet && geolocalisationOutlet.length === 0) {
            geolocalisationOutlet.createComponent(compFactory);
        }
    });
}

<div *ngIf="section === 'geolocalisation'" id="geolocalisation">
     <div #geolocalisationOutlet></div>
</div>

其他回答

另一个快速的“技巧”(简单的解决方案)是使用[hidden]标签而不是*ngIf,重要的是要知道,在这种情况下,Angular会构建对象并在类下绘制它:隐藏,这就是ViewChild工作没有问题的原因。 所以重要的是要记住,你不应该在沉重或昂贵的物品上使用隐藏,这会导致性能问题

  <div class="addTable" [hidden]="CONDITION">

对于Angular 8——混合了null检查和@ViewChild static:错误的黑客

用于等待异步数据的分页控件

@ViewChild(MatPaginator, { static: false }) set paginator(paginator: MatPaginator) {
  if(!paginator) return;
  paginator.page.pipe(untilDestroyed(this)).subscribe(pageEvent => {
    const updated: TSearchRequest = {
      pageRef: pageEvent.pageIndex,
      pageSize: pageEvent.pageSize
    } as any;
    this.dataGridStateService.alterSearchRequest(updated);
  });
}

我们遇到了在*ngIf上设置tabindex的情况

html:

<div #countryConditional1 *ngIf="country=='USA'">                        
  <input id="streetNumber" [(ngModel)]="streetNumber" pInputText>
</div>

ts:

@ViewChild('countryConditional1') set countryConditional1(element) {
  if (element) {
    const container2 = document.querySelector("#someElement");
    container2.querySelector("span > input").setAttribute("tabindex", "18");
  }

解决这个问题的另一种方法是手动运行更改检测器。

首先注入ChangeDetectorRef:

constructor(private changeDetector : ChangeDetectorRef) {}

然后在更新控制*ngIf的变量后调用它

show() {
        this.display = true;
        this.changeDetector.detectChanges();
    }

在我的例子中,我只需要在模板中存在div时加载整个模块,这意味着出口在ngif中。这样,每当angular检测到#geolocalisationOutlet元素时,它就会在其中创建一个组件。该模块也只加载一次。

constructor(
    public wlService: WhitelabelService,
    public lmService: LeftMenuService,
    private loader: NgModuleFactoryLoader,
    private injector: Injector
) {
}

@ViewChild('geolocalisationOutlet', {read: ViewContainerRef}) set geolocalisation(geolocalisationOutlet: ViewContainerRef) {
    const path = 'src/app/components/engine/sections/geolocalisation/geolocalisation.module#GeolocalisationModule';
    this.loader.load(path).then((moduleFactory: NgModuleFactory<any>) => {
        const moduleRef = moduleFactory.create(this.injector);
        const compFactory = moduleRef.componentFactoryResolver
            .resolveComponentFactory(GeolocalisationComponent);
        if (geolocalisationOutlet && geolocalisationOutlet.length === 0) {
            geolocalisationOutlet.createComponent(compFactory);
        }
    });
}

<div *ngIf="section === 'geolocalisation'" id="geolocalisation">
     <div #geolocalisationOutlet></div>
</div>