5
votes

I have 2 router outlets: sidebar and the default one.

 {path: 'motor/:id', component: MotorComponent}

From the sidebar outlet i have some router links to the default router outlet that target the same component (MotorComponent) but different param.

this.router.navigate(['./', {outlets: {primary: ['motor', 
id]}}]);

If i click on one, the respective component is loaded but if i click on another router link the correct component is not loaded. The param changes in the url.

I read it is a common problem. I tried with a reuse strategy but i don't think it was well implemented, nothing happened.

I would like to click on different router links that target the same component but different params and the component would load even if it is the same component.

This is my ngOninit where i subscribe to route params, save that id and then fetch my new object from the service.

motor: MotoarePrincipale;
id: number;

ngOnInit() {
this.route.params.subscribe(
  (params: Params) => {
    this.id = +params['id'];
  }
);
this.masinaService.getMotorByMotorId(this.id).subscribe(data => {
  this.motor = data;
});
2
Have you tried subscribing to route params in the ngOnInit funtion() this.route.params.subscribe(params => ....) ? - noobed
Yes, i am already subscribing to route.params.subscribe because i save that id. I need that id to fetch from my service the new object. What are you referring to? - gxg
could you provide a little more code to elaborate on? maybe your ngOnInit or constructor, whichever defines and holds your observables and subscriptions - noobed
I added my ngOnInit! - gxg
i will analyze it. for the moment i managed to set my motor object after i subscribe to the route params and fetch the motor object from my service, as you suggested. The object changes and this is what i want for the moment. - gxg

2 Answers

14
votes

for angular 7(I tried only on this version) you could use in your component the route reuse strategy:

constructor(private routerR: Router) {

    // this is for routerLink on same component when only queryParameter changes
    this.routerR.routeReuseStrategy.shouldReuseRoute = function () {
      return false;
    };
  }
0
votes

You need to use

private route: ActivatedRoute,
private router: Router

You need to listen for id changes in your url, and finally to switch map to correct id. Look at this example from RXJS Observables in Angular - Ward Bell & Sander Ellis

Here an implementation example, where you can see movies based on their id without refreshing the page:

TS:

import { Component, OnDestroy, OnInit } from '@angular/core';
import { ActivatedRoute, Router } from '@angular/router';

import { Observable, Subscription } from 'rxjs';
import { switchMap, tap, map } from 'rxjs/operators';

import { Movie } from '../../samples/sw-interfaces';
import { FilmService } from './film.service';

@Component({
  selector: 'app-movie',
  template: `
      <h1>Routed Movie Component</h1>

      <div>
        <button (click)="previousMovie()">Previous</button>

        <div *ngIf="currentMovie$ | async as movie; else noMovie"  class="title">
            ({{movie.id}}) {{ movie.title }}
        </div>

        <ng-template #noMovie>
            <div class="missing">No Movie</div>
        </ng-template>

        <button (click)="nextMovie()">Next</button>
      </div>
    `,
  providers: [FilmService],
  styleUrls: ['./movie.component.css']
})
export class MovieComponent implements OnInit, OnDestroy {
  currentMovie$: Observable<Movie>;

  currentId: number;
  nextId: number;
  previousId: number;

  routerEventsSubscription: Subscription;

  constructor(
    private filmService: FilmService,
    private route: ActivatedRoute,
    private router: Router) { }

  ngOnInit(): void {
    this.listenToRouteParams();
    this.listenToRouterEvents();
  }

  private listenToRouteParams() {
    // ActivatedRoute.paramMap observable changes whenever the URL's id changes
    this.currentMovie$ = this.route.paramMap.pipe(

      // extract film id parameter
      map(params => params.get('id')),

      // switchMap because can discard in-flight request for a new id
      switchMap(id => {
        return this.filmService.getFilm(id).pipe(
          tap(movie => this.currentId = movie ? movie.id : 0)
        );
      })
    );
  }

  nextMovie() {
    this.navigate(this.currentId + 1);
  }

  previousMovie() {
    this.navigate(this.currentId - 1);
  }

  navigate(id: number) {
    id = id || 1;
    this.router.navigate([`/movie/${id}`]);
  }




  /// ROUTER EVENTS ////
  private listenToRouterEvents() {
    // Listen to the router do its thing
    // What is `routerEventsSubscription`?
    this.routerEventsSubscription = this.router.events.subscribe(event => {
      console.log('Router event: ', event);
    });
    console.log('MovieComponent initialized');
  }

  ngOnDestroy(): void {
    console.log('MovieComponent destroyed');

    // Question: Why must we unsubscribe from the router?
    // Question: Why do we NOT unsubscribe from `route.paramMap`
    this.routerEventsSubscription.unsubscribe();
  }

}

SERVICE:

// tslint:disable:member-ordering
import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core';

import { of } from 'rxjs';
import { catchError, map } from 'rxjs/operators';

import { Movie, RootObject } from './../sw-interfaces';
import { SwUrlService } from '../../samples/sw-url.service';

@Injectable()
export class FilmService {
    constructor(private http: HttpClient, private swUrlService: SwUrlService) {}

    getFilm(id: string) {
      return this.http.get<RootObject<Movie>>(`${this.url}/${id}`)
        .pipe(
          map(data => data.results),
          catchError(err => {
            if (err.status === 404) {
              return of(undefined); // OK if not found.
            }

            // log HTTP error and ...
            console.error('GET failed', err)
            // rethrow as a user-friendly message
            throw new Error('Sorry but can\'t get movies right now; please try ain  later');
          })
        );
    }

    private get url() {
        return this.swUrlService.url;
    }
}