Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Angular 4 Component ngOnInit not called on each route request

I just recently started diving into Angular 4 coming from Angular 1.5. I'm working with user routes and pulling API data from a service into a user component.

The component seems to stay static unlike controllers in 1.* where they were refreshed on each request.

Is there anyway to have the ngOnInit function called on each new route request?

My user component class:

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

UPDATE:

Found a solution that I think works best for my scenario. Based on several answers and comments to my question and further investigation, subscribing to the route seems to be the way to go. Here is my updated component:

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

}
like image 280
Rob Avatar asked May 05 '17 13:05

Rob


People also ask

How many times will ngOnInit () be called in a component?

Right now, if an error happens during detecting changes of content/view children of a component, ngOnInit will be called twice (seen in DynamicChangeDetector). This can lead to follow up errors that hide the original error.

Is ngOnInit called on route change?

If in the constructor you subscribe to the active route, ngInit will be called every time the router navigates to that page.

How do I call ngOnInit again from another component?

ngOnInit called once the component is created. so you can create a function and call the function again.

Is ngOnInit called only once?

NGOnInit is named after the indexing of the target sites for the first time, and before any of its children are tested. Only once a guide is included.


3 Answers

Angular prefers -by default- to reuse the same component instead of instanciating a new one if the route is the same but the parameters change.

Good news ! this is a customizable behavior, you can force instanciating a new component instance by implementing your own 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';
  }
}

in your module:

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

(this code is more or less taken from this article, especially the check for the name property which might not be very accurate...)

Note that ther might be some performance drawback when reinstanciating the same component. So maybe you'd better use the observables properties instead of the snapshot.

like image 185
n00dl3 Avatar answered Oct 01 '22 18:10

n00dl3


I realize that this is quite after the fact, but I thought it best not to leave the an incomplete solution that newcomers might implement into their projects.

In response to the original post's update: it is important that you unsubscribe from Observables once you're done using them, otherwise you risk leaving references in memory. Here's a bare example.

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
  }
}
like image 30
Acorath Avatar answered Oct 01 '22 17:10

Acorath


Hi you have to use switchMap like this:

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

This work for me.

like image 21
Francisco Avatar answered Oct 01 '22 18:10

Francisco