【问题标题】:Angular 4 Component ngOnInit not called on each route requestAngular 4 Component ngOnInit 未在每个路由请求上调用
【发布时间】:2017-10-04 00:38:37
【问题描述】:

我最近才开始从 Angular 1.5 开始深入研究 Angular 4。我正在使用用户路由并将 API 数据从服务中提取到用户组件中。

与 1.* 中的控制器不同,组件似乎保持静态,每次请求都会刷新它们。

是否有在每个新路由请求上调用 ngOnInit 函数?

我的用户组件类:

// url structure: /user/:id
export class UserComponent implements OnInit {

    constructor(private route: ActivatedRoute) {}

    ngOnInit() {

      // doesn't log new id on route change
      let id = this.route.snapshot.params['id'];
      console.log(id);

      this.route.params
        .switchMap(params => this.userService.getUser(params['id']))
        .subscribe(user => {
          // logs new id on route change
          let id = this.route.snapshot.params['id'];
          console.log(id);

          this.user = user
        });
    }
}

更新

找到了一个我认为最适合我的方案的解决方案。根据我的问题和进一步调查的几个答案和 cmets,订阅路线似乎是要走的路。这是我更新的组件:

export class UserComponent {

  user;
  id;

  constructor(
    private userService: UserService,
    private route: ActivatedRoute
  ) {}


  ngOnInit() {
    this.route.params
      .subscribe(params => this.handleRouteChange(params));
  }

  handleRouteChange(params) {
    this.id = params['id'];

    switch (this.id) {
      case '1':
        console.log('User 1');
        break;

       case '2':
        console.log('User 2');
        break;

       case '3':
        console.log('User 3');
        break;
    }

    this.userService.getUser(this.id).
      subscribe(user => this.user = user);
  }

}

【问题讨论】:

标签: angular angular2-routing angular2-components


【解决方案1】:

url 看起来像“...?id=1”还是“../:id”,并且您希望组件在每次 GET 参数更改时记录 id?问题在here 讨论。我发现一个更好的解决方案是here。我自己没有测试过,但这应该可以。

第二个链接显示了如何订阅组件内的路由更改,我认为这基本上是您想要做的。它将允许您处理 ngOnInit 内的 GET 参数更改。我不确定当您最初导航到 URL 时路由订阅是否会运行,因此您可能希望处理路由事件的函数调用 doCheck(),并将 ngOnInit 更改为 ngAfterContentInit

ngOnInit 仅在change detection 上运行,GET 更改似乎不会触发它,这让我感到惊讶。

Tl;dr - 在构造函数中订阅路由更改事件并创建一个处理发出事件的函数。该函数应该包含您在 ngOnInit() 中的逻辑。

【讨论】:

  • 这是一个不好的建议,解决方案是不使用快照,而只使用可观察对象。或者实现自定义RouteReuseStrategy
  • 啊废话,我没仔细看。我给了一个不好的答案。不要使用快照。使用 n00dl3 所说的 observable。您可以在我链接的 stackoverflow 中看到一个示例,但忽略快照逻辑。只看 ngOnInit() 逻辑。我已经编辑了第二个链接,只显示路由事件订阅逻辑。
  • 感谢您的回答,但我相信我找到了解决方案。我用它更新了我的答案。似乎最有意义。
【解决方案2】:

如果路由相同但参数发生变化,Angular 更喜欢 - 默认情况下 - 重用相同的组件而不是实例化一个新组件。

好消息!这是一种可自定义的行为,您可以通过实现自己的RouteReuseStrategy 来强制实例化一个新的组件实例:

export class MyRouteReuseStrategy extends DefaultRouteReuseStrategy {
  shouldReuseRoute(next: ActivatedRouteSnapshot, current: ActivatedRouteSnapshot): boolean {
    let name = next.component && (next.component as any).name;
    return super.shouldReuseRoute(next, current) && name !== 'UserComponent';
  }
}

在你的模块中:

@NgModule({
  ...
  providers:[{
      provide: RouteReuseStrategy,
      useClass: MyRouteReuseStrategy}]
  ...
})
export class AppModule {}

(此代码或多或少取自this article,尤其是对name 属性的检查可能不太准确...)

请注意,重新实例化同一组件时可能会出现一些性能缺陷。所以也许你最好使用 observables 属性而不是快照。

【讨论】:

  • 我找到了解决该问题的方法并用它更新了我的答案。我相信我找到的解决方案既可以保留性能又可以处理每个请求的参数。我很高兴听到您可能对它有任何建议或建议。谢谢!
  • 在 Angular 2.3+ 中已经改变了一些东西,现在核心中的 RouteReuseStrategy 是针对 DefaultRouteReuseStrategy 的,你可以像这样使用它:
    shouldReuseRoute(future: ActivatedRouteSnapshot, curr: ActivatedRouteSnapshot): boolean { let name = future.component && (future.component as any).name; return future.routeConfig === curr.routeConfig && name !== 'UserComponent'; }
    你可以在这里看到更好medium.com/@gerasimov.pk/…
【解决方案3】:

您好,您必须像这样使用 switchMap

this.sub = this.route.paramMap
  .switchMap((params: ParamMap) =>
  this.firebaseService.getProductsByCategory(params.get('category'))).subscribe(products => {
  this.products = products;
  this.count = products.length;
});

这对我有用。

【讨论】:

    【解决方案4】:

    我意识到这是事后的事,但我认为最好不要留下一个不完整的解决方案,新手可能会在他们的项目中实施。

    响应原帖的更新:在使用完 Observables 后取消订阅 Observables 很重要,否则可能会将引用留在内存中。这是一个简单的例子。

    import { Component, OnInit, OnDestroy } from '@angular/core';
    import { ActivatedRoute, Params } from '@angular/router';
    import { Subscription, Observable } from 'rxjs';
    
    @Component({
      selector: 'app-example',
      templateUrl: './example.component.html',
      styleUrls: ['./example.component.scss']
    })
    export class Example implements OnInit, OnDestroy {
      protected _routeChangeSubscription: Subscription;
    
      constructor(
        protected _activatedRoute: ActivatedRoute,
      ) { }
    
      ngOnInit(): void {
        this._routeChangeSubscription = this._activatedRoute.params.subscribe(this._routeChangeHandler);
      }
    
      ngOnDestroy(): void {
        this._routeChangeSubscription.unsubscribe();
      }
    
      public _routeChangeHandler = (params: Observable<Params>) => {
        // check params here
      }
    }
    

    【讨论】:

      猜你喜欢
      • 2018-06-22
      • 2019-07-21
      • 2017-11-03
      • 2018-08-23
      • 2018-10-23
      • 2018-04-28
      • 2016-06-26
      • 2017-04-08
      • 1970-01-01
      相关资源
      最近更新 更多