【问题标题】:Why is this ElementRef undefined after navigating away and return back?为什么这个 ElementRef 在导航离开并返回后未定义?
【发布时间】:2018-08-06 12:18:12
【问题描述】:

我在我的一个组件 (favorite-places) 中引用了 @ViewChild,这是 Google 地图自动完成功能的搜索输入字段。

一切正常,除非我导航到另一个位置并返回favorite-places

这是组件的布局。请注意,有一个 [couterLink]="['../select']" 可以让我导航到另一个位置。

<div *headerContent fxFlex fxLayout="row" fxLayoutAlign="space-between center"
     style="padding-left: 5px; padding-right: 5px;">    

  <div fxLayoutAlign="center center" style="">
    <mat-icon>search</mat-icon>
    <mat-form-field class="form-group">
      <input #search matInput placeholder="Search" autocorrect="off" autocapitalize="off" spellcheck="off" type="text" class="form-control" [formControl]="searchControl">
    </mat-form-field>
  </div>

  <button mat-icon-button [routerLink]="['../select']" [queryParams]="{groupId: groupId}" (click)="addPlace()">
    <mat-icon>check</mat-icon>
  </button>    
</div>

<div>

  <div class="container">
    <agm-map [latitude]="latitude" [longitude]="longitude" [scrollwheel]="false" [zoom]="zoom" style="height:150px; margin-top: 15px;">
      <agm-marker [latitude]="latitude" [longitude]="longitude"></agm-marker>
    </agm-map>
  </div>

  <div>
    <span>PlaceID: {{selectedPlace?.place_id}}</span><br/>
    <span>Name: {{selectedPlace?.name}}</span>
  </div>

</div>

正如我所说,一切正常,除了当我进入这个地方,离开它然后返回它。在这种情况下,我会看到这个错误:

ERROR Error: Uncaught (in promise): TypeError: Cannot read property 'nativeElement' of undefined
TypeError: Cannot read property 'nativeElement' of undefined
    at favorite-places.component.ts:61
    at ZoneDelegate.push../node_modules/zone.js/dist/zone.js.ZoneDelegate.invoke (zone.js:388)
    at Object.onInvoke (core.js:4062)
    at ZoneDelegate.push../node_modules/zone.js/dist/zone.js.ZoneDelegate.invoke (zone.js:387)
    at Zone.push../node_modules/zone.js/dist/zone.js.Zone.run (zone.js:138)
    at zone.js:872
    ...

这是发生错误的地方:

this.mapsAPILoader.load().then(() => {
    let autocomplete = new google.maps.places.Autocomplete(
      this.searchElementRef.nativeElement, 
      { types: ['establishment'] });

    autocomplete.addListener('place_changed', () => {
      this.ngZone.run(() => {
        // ..
      });
    });
  }
);
export class FavoritePlacesComponent implements OnInit {

  // ..

  @ViewChild("search")
  public searchElementRef: ElementRef;

}

似乎this.searchElementRef 在离开该位置后被取消或未定义。然而,回到它并不会重新创建它。这对我来说似乎是一个生命周期问题,但我不知道如何解决。

【问题讨论】:

  • 你是在访问 ngAfterViewInit() 中的 nativeElement 吗?
  • @SureshKumarAriya 不。我现在没有实现这个功能。
  • 只有在屏幕上渲染 DOM 时才能访问 viewchild 元素属性。所以请在 ngAfterViewInit() 方法中添加。

标签: angular


【解决方案1】:

您需要将nativeElement 的用法包含在ngAfterViewInit 生命周期挂钩中。

您的代码首次运行的原因是 load() 是异步的,并且您的回调在下一个 VM 轮次中调用,并且组件已经呈现。但是在返回到同一路线之后 Maps API 已经加载,所以从 load() 返回的承诺同步解析 - 在 ngAfterViewInit 之前。

加载是否应该始终是异步的以消除歧义是值得商榷的。

【讨论】:

  • 我已经尝试过了,但不幸的是它并没有解决问题。我自己发布了一个答案。这似乎与我在导航事件期间替换标题的事实有关,这似乎破坏了引用,并且似乎由于某种原因它没有被重新创建(虽然不知道为什么)。
【解决方案2】:

我又一次陷入了可替换标题的陷阱。

请注意,我使用的是HeaderContentDirective,它会撕下标题并在每个导航事件上重新创建它。

<div *headerContent ..>
    <!-- .. -->
</div>

..

@Directive({
  selector: '[headerContent]',
})
export class HeaderContentDirective implements OnInit {
  constructor(private headerContent: HeaderContentService, private templateRef: TemplateRef<any>) {}

  ngOnInit(): void {
    this.headerContent.setContents(this.templateRef);
  }
}

还有我的ApplicationComponent

  ngAfterViewInit(): void {
    this
      .headerContentService
      .contents
      .subscribe(ref => {
        setTimeout(() => {

          if (this.currentHeaderView !== null) {
            this.logger.trace('Destroying currentHeaderView header ..');
            this.currentHeaderView.destroy();
            this.currentHeaderView = null;
          }
          if (ref === null) {
            this.logger.trace('No new header reference ..');
            return;
          }
          this.logger.trace('Creating embedded view ..');
          this.currentHeaderView = this.vcr.createEmbeddedView(ref);
        });
      });
  }

这似乎不允许我在这里做我想做的事,这也意味着我必须将自动完成输入移到其他地方或寻找其他解决方法。

【讨论】:

  • 尝试使用 ngAfterViewInit 和地图加载器将 mat-form-field 包含在自己的组件中
  • 我不完全了解 Angular 渲染,但我认为整个设计容易出现源自组件树遍历顺序等的细微错误。
  • @TomaszBłachut 我可能会尝试一下,但现在我只想在实现我的应用程序的主要功能方面取得进展。如有必要,我稍后会尝试对此进行优化。不过感谢您的帮助!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-01-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多