Angular 4 Component ngOnInit未在每个路径请求上调用

时间:2017-05-05 13:38:25

标签: angular angular2-routing angular2-components

我刚刚开始潜入Angular 4来自Angular 1.5。我正在处理用户路由并将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
        });
    }
}

更新

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

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);
  }

}

4 个答案:

答案 0 :(得分: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属性而不是快照。

答案 1 :(得分:1)

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

为响应原始帖子的更新:使用完可观察对象后,请取消其订阅,这一点很重要,否则您就有将引用保留在内存中的风险。这是一个简单的例子。

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
  }
}

答案 2 :(得分:0)

网址是否看起来像“...?id = 1”或“../:id”,并且您希望每次GET参数更改时组件都记录该ID?讨论的问题是here。我找到的更好的解决方案是here。我自己没有测试过,但这应该有效。

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

ngOnInit仅在change detection上运行,并且GET更改似乎不会触发它,这对我来说是令人惊讶的。

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

答案 3 :(得分:0)

您必须使用 switchMap ,如下所示:

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

这项工作适合我。