Comment détecter un changement d'itinéraire dans Angular?


428

Je cherche à détecter un changement d'itinéraire dans mon AppComponent.

Par la suite, je vérifierai le jeton utilisateur global pour voir s'il est connecté. Ensuite, je pourrai rediriger l'utilisateur s'il n'est pas connecté.

Réponses:


534

Dans Angular 2, vous pouvez subscribe(événement Rx) vers une instance de routeur. Vous pouvez donc faire des choses comme

class MyClass {
  constructor(private router: Router) {
    router.subscribe((val) => /*whatever*/)
  }
}

Modifier (depuis rc.1)

class MyClass {
  constructor(private router: Router) {
    router.changes.subscribe((val) => /*whatever*/)
  }
}

Edit 2 (depuis 2.0.0)

voir aussi: doc Router.events

class MyClass {
  constructor(private router: Router) {
    router.events.subscribe((val) => {
        // see also 
        console.log(val instanceof NavigationEnd) 
    });
  }
}

2
Je suis en mesure d'obtenir des données en event._root.children[0].value._routeConfig.dataespérant qu'il puisse y avoir une meilleure façon
Akshay

6
@Akshay avez-vous vu cet article de Todd Motto: [Titres de pages dynamiques dans Angular 2 avec des événements de routeur] ( toddmotto.com/dynamic-page-titles-angular-2-router-events )
Bogac

10
pourquoi tire-t-il 3 fois?
Toolkit

2
@Toolkit son parce que l'événement a 3 états et dans un changement d'url réussi. Les 3 états sont: 'NavigationStart', 'NavigationEnd' et 'RoutesRecognized'
RicardoGonzales

10
vous pouvez filtrer facilement les événements avec l' filteropérateur RxJS . router.events.pipe(filter(e => e instanceof NavigationEnd).subscribe((e) => { ... }
Simon_Weaver

314

RxJS 6

router.events.pipe(filter(event => event instanceof NavigationStart))

Merci à Peilonrayz (voir les commentaires ci-dessous)

nouveau routeur> = RC.3

import { Router, NavigationStart, NavigationEnd, NavigationError, NavigationCancel, RoutesRecognized } from '@angular/router';

constructor(router:Router) {
  router.events.forEach((event) => {
    if(event instanceof NavigationStart) {
    }
    // NavigationEnd
    // NavigationCancel
    // NavigationError
    // RoutesRecognized
  });
}

Vous pouvez également filtrer par l'événement donné:

import 'rxjs/add/operator/filter';

constructor(router:Router) {
  router.events
    .filter(event => event instanceof NavigationStart)
    .subscribe((event:NavigationStart) => {
      // You only receive NavigationStart events
    });
}

L'utilisation de l' pairwiseopérateur pour obtenir l'événement précédent et actuel est également une bonne idée. https://github.com/angular/angular/issues/11268#issuecomment-244601977

import 'rxjs/add/operator/pairwise';
import { Router } from '@angular/router;

export class AppComponent {
    constructor(private router: Router) {
        this.router.events.pairwise().subscribe((event) => {
            console.log(event);
        });
    };
}

1
@GunterZochbauer au lieu de «est» j'utiliserais «instanceof». Et 'event: Event' devrait être entre parenthèses. Merci pour cette nouvelle fonctionnalité assez puissante! Je l'aime
Maxim

2
Cela jette une erreur de compilation sur les versions actuellesArgument of type '(event: Event) => void' is not assignable to parameter of type
Rudi Strydom

1
@RudiStrydom & Günter Zöchbauer - l' Argument of type '(event: Event) => void' is not assignable to parameter of typeerreur est due au fait que dans votre extrait de filtre, vous vous abonnez à un objet de type Event au lieu de NavigationEvent.
Bonnici

1
Le deuxième échantillon doit être NavigationEvent au lieu d'Event. N'oubliez pas non plus d'importer "Event as NavigationEvent" depuis @ angular / router
Mick

1
L'astuce concernant l'importation était destinée à tous ceux qui souhaitaient résoudre cette erreur :)
Mick

92

Pour Angular 7, quelqu'un devrait écrire comme:

this.router.events.subscribe((event: Event) => {})


Un exemple détaillé peut être le suivant:

import { Component } from '@angular/core'; 
import { Router, Event, NavigationStart, NavigationEnd, NavigationError } from '@angular/router';

@Component({
    selector: 'app-root',
    template: `<router-outlet></router-outlet>`
})
export class AppComponent {

    constructor(private router: Router) {

        this.router.events.subscribe((event: Event) => {
            if (event instanceof NavigationStart) {
                // Show loading indicator
            }

            if (event instanceof NavigationEnd) {
                // Hide loading indicator
            }

            if (event instanceof NavigationError) {
                // Hide loading indicator

                // Present error to user
                console.log(event.error);
            }
        });

   }
}

2
C'est bien! très complet! a parfaitement fonctionné sur Angular 7.
MaylorTaylor

1
Habituellement, la navigation elle-même prend très peu de temps si vous utilisez une stratégie de préchargement. En termes de convivialité, j'utiliserais des indicateurs de chargement uniquement sur les requêtes http backend, voire pas du tout.
Phil

5
Dans le constructeur , vous ne devez pas utiliser <this>, votre cas est pour ngOnInit.
Sergio Reis

1
Parfait, comment puis-je obtenir le param.id exact de l'URL?
ShibinRagh

2
La solution n'est pas limitée aux composants, elle se propage dans toute l'application, consommant des ressources
Md. Rafee

54

Angulaire 7 , si vous voulez subscribelarouter

import { Router, NavigationEnd } from '@angular/router';

import { filter } from 'rxjs/operators';

constructor(
  private router: Router
) {
  router.events.pipe(
    filter(event => event instanceof NavigationEnd)  
  ).subscribe((event: NavigationEnd) => {
    console.log(event.url);
  });
}

2
il ne capture pas l'événement de redirection
Anandhu Ajayakumar

40

Angulaire 4.x et supérieur:

Ceci peut être réalisé en utilisant la propriété url de la classe ActivatedRoute comme ci-dessous,

this.activatedRoute.url.subscribe(url =>{
     console.log(url);
});

Remarque: que vous devez importer et injecter le fournisseur à partir du angular/routerpackage

import { ActivatedRoute } from '@angular/router`

et

constructor(private activatedRoute : ActivatedRoute){  }

18

Le routeur 3.0.0-beta.2 devrait être

this.router.events.subscribe(path => {
  console.log('path = ', path);
});

cela fonctionne pour le chemin actuel, mais qu'en est-il du précédent?
tatsu

16

En angulaire 6 et RxJS6:

import { filter, debounceTime } from 'rxjs/operators';

 this.router.events.pipe(
      filter((event) => event instanceof NavigationEnd),
      debounceTime(40000)
    ).subscribe(
      x => {
      console.log('val',x);
      this.router.navigate(['/']); /*Redirect to Home*/
}
)

3
vous avez raté l'importation pour Routerimport {Router, NavigationEnd} from "@angular/router"
Damir Beylkhanov

15

Les réponses ici sont correctes pour router-deprecated. Pour la dernière version de router:

this.router.changes.forEach(() => {
    // Do whatever in here
});

ou

this.router.changes.subscribe(() => {
     // Do whatever in here
});

Pour voir la différence entre les deux, veuillez consulter cette question SO .

Éditer

Pour les dernières, vous devez faire:

this.router.events.subscribe(event: Event => {
    // Handle route change
});

Fournit-il des données sur l'itinéraire précédent et actuel?
akn

Le routera été mis à jour à nouveau (je n'ai pas encore mis à jour ma réponse), donc je ne sais pas comment c'est pour la dernière. Pour ce dont routerj'ai parlé, vous ne pouviez pas. @akn
Dehli

Pouvez-vous fournir un contexte pour cette réponse? quelles lignes remplacez-vous dans les autres solutions par la vôtre?
Gerard Simpson

12

Dans Angular 8, vous devriez faire comme this.router.events.subscribe((event: Event) => {})

Exemple:

import { Component } from '@angular/core'; 
import { Router, Event } from '@angular/router';
import { NavigationStart, NavigationError, NavigationEnd } from '@angular/router';

@Component({
    selector: 'app-root',
    template: `<router-outlet></router-outlet>`
})
export class AppComponent {

    constructor(private router: Router) {
        //Router subscriber
        this.router.events.subscribe((event: Event) => {
            if (event instanceof NavigationStart) {
                //do something on start activity
            }

            if (event instanceof NavigationError) {
                // Handle error
                console.error(event.error);
            }

            if (event instanceof NavigationEnd) {
                //do something on end activity
            }
        });
   }
}

10

Dans le composant, vous pouvez essayer ceci:

import {NavigationEnd, NavigationStart, Router} from '@angular/router';

constructor(private router: Router) {
router.events.subscribe(
        (event) => {
            if (event instanceof NavigationStart)
                // start loading pages
            if (event instanceof NavigationEnd) {
                // end of loading paegs
            }
        });
}

8

Capturez les événements de changement d'itinéraire de la manière suivante ...

import { Component, OnInit, Output, ViewChild } from "@angular/core";
import { Router, NavigationStart, NavigationEnd, Event as NavigationEvent } from '@angular/router';

@Component({
    selector: "my-app",
    templateUrl: "app/app.component.html",
    styleUrls: ["app/app.component.css"]
})
export class AppComponent {

    constructor(private cacheComponentObj: CacheComponent,
        private router: Router) {

        /*  Route event types
            NavigationEnd
            NavigationCancel
            NavigationError
            RoutesRecognized
        */
        router.events.forEach((event: NavigationEvent) => {

            //Before Navigation
            if (event instanceof NavigationStart) {
                switch (event.url) {
                case "/app/home":
                {
                    //Do Work
                    break;
                }
                case "/app/About":
                {
                    //Do Work
                    break;
                }
                }
            }

            //After Navigation
            if (event instanceof NavigationEnd) {
                switch (event.url) {
                case "/app/home":
                {
                    //Do Work
                    break;
                }
                case "/app/About":
                {
                    //Do Work
                    break;
                }
                }
            }
        });
    }
}

Parfait, comment puis-je obtenir le param.id exact de l'URL?
ShibinRagh

6

La localisation fonctionne ...

import {Component, OnInit} from '@angular/core';
import {Location} from '@angular/common';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.scss']
})

export class AppComponent implements OnInit {

    constructor(private location: Location) {
        this.location.onUrlChange(x => this.urlChange(x));
    }

    ngOnInit(): void {}

    urlChange(x) {
        console.log(x);
    }
}

Bonne réponse. cela fonctionne pour mon cas. Merci
Umar Tariq

4

au-dessus de la plupart des solutions correctes, mais je suis confronté à un problème qui émet plusieurs fois l'événement "Emission de navigation". Hear est donc la solution complète pour Angular 6.

import { Subscription } from 'rxjs/Subscription';
import 'rxjs/add/operator/do';
import 'rxjs/add/operator/filter';    

export class FooComponent implements OnInit, OnDestroy {
   private _routerSub = Subscription.EMPTY;
   constructor(private router: Router){}

   ngOnInit(){
     this._routerSub = this.router.events
      .filter(event => event instanceof NavigationEnd)
      .subscribe((value) => {
         //do something with the value
     });
  }

  ngOnDestroy(){
   this._routerSub.unsubscribe();
  }
} 

3

@Ludohen est une bonne réponse, mais au cas où vous ne voudriez pas utiliser, instanceofutilisez ce qui suit

this.router.events.subscribe(event => {
  if(event.constructor.name === "NavigationStart") {
    // do something...
  }
});

de cette façon, vous pouvez vérifier le nom de l'événement actuel sous forme de chaîne et si l'événement s'est produit, vous pouvez faire ce que vous avez prévu de faire votre fonction.


3
pourquoi ne pas utiliser la sécurité dactylographiée?
Pascal

@Pascal pourquoi la haine? et le Eventtype provoque une erreur dans Atom c'est pourquoi je ne l'ai pas utilisé
Khaled Al-Ansari

2
@Pascal non c'est un problème angulaire puisque l'événement routeur n'est pas le même que l'événement navigateur et c'est pourquoi le type d'événement ne fonctionnera pas! ils ont besoin de créer une nouvelle interface pour cet événement, j'aurais dû le dire depuis le début, mais le vote déraisonnable n'a pas aidé :)
Khaled Al-Ansari

5
comme la minification est effectuée dans le code de production, vous devriez plutôt l'utiliser instanceOfpour que votre exemple fonctionne également dans le code de production. if(event instanceOf NavigationStart) {
Milan Jaric

1
Devrait êtreif(event instanceof NavigationStart)
Ketan

1

Je travaille avec l'application angular5 et je suis confronté au même problème. quand je passe par la documentation angulaire, ils fournissent la meilleure solution pour gérer les événements du routeur. Vérifiez la documentation suivante.

Représente un événement déclenché lorsqu'une navigation se termine avec succès

Comment l'utiliser?

import { Component, OnInit } from '@angular/core';
import { Router, ActivatedRouteSnapshot, NavigationEnd } from '@angular/router';
@Component({
    selector: 'app-navbar',
    templateUrl: './navbar.component.html',
    styleUrls: ['./navbar.component.css']
})
export class NavbarComponent implements OnInit {
    constructor(private router: Router) { }
    ngOnInit(): void {
        //calls this method when navigation ends
        this.router.events.subscribe(event => {
            if (event instanceof NavigationEnd) {
                //calls this stuff when navigation ends
                console.log("Event generated");
            }
        });
    }
}

Quand l'utiliser?

Dans mon cas, mon application partage un tableau de bord commun pour tous les utilisateurs tels que les utilisateurs, les administrateurs, mais j'ai besoin d'afficher et de masquer certaines options de barre de navigation selon les types d'utilisateurs.

C'est pourquoi chaque fois que l'url change, je dois appeler la méthode de service qui renvoie les informations utilisateur connectées conformément à la réponse, j'irai pour d'autres opérations.


0

Les types d'œuvres suivants peuvent faire l'affaire pour vous.

// in constructor of your app.ts with router and auth services injected
router.subscribe(path => {
    if (!authService.isAuthorised(path)) //whatever your auth service needs
        router.navigate(['/Login']);
    });

Malheureusement, cela redirige plus tard dans le processus de routage que je ne le souhaiterais. Le onActivate()composant cible d'origine est appelé avant la redirection.

Il y a un @CanActivatedécorateur que vous pouvez utiliser sur le composant cible mais c'est a) non centralisé et b) ne bénéficie pas des services injectés.

Ce serait formidable si quelqu'un pouvait suggérer une meilleure façon d'autoriser centralement un itinéraire avant qu'il ne soit validé. Je suis sûr qu'il doit y avoir un meilleur moyen.

Voici mon code actuel (comment pourrais-je le changer pour écouter le changement d'itinéraire?):

import {Component, View, bootstrap, bind, provide} from 'angular2/angular2';
import {ROUTER_BINDINGS, RouterOutlet, RouteConfig, RouterLink, ROUTER_PROVIDERS, APP_BASE_HREF} from 'angular2/router';    
import {Location, LocationStrategy, HashLocationStrategy} from 'angular2/router';

import { Todo } from './components/todo/todo';
import { About } from './components/about/about';

@Component({
    selector: 'app'
})

@View({
    template: `
        <div class="container">
            <nav>
                <ul>
                    <li><a [router-link]="['/Home']">Todo</a></li>
                    <li><a [router-link]="['/About']">About</a></li>
                </ul>
            </nav>
            <router-outlet></router-outlet>
        </div>
    `,
    directives: [RouterOutlet, RouterLink]
})

@RouteConfig([
    { path: '/', redirectTo: '/home' },
    { path: '/home', component: Todo, as: 'Home' },
    { path: '/about', component: About, as: 'About' }
])

class AppComponent {    
    constructor(location: Location){
        location.go('/');
    }    
}    
bootstrap(AppComponent, [ROUTER_PROVIDERS, provide(APP_BASE_HREF, {useValue: '/'})]);

J'ai vu des gens étendre le routerOutlet pour ajouter leur code d'authentification qui est à sens unique. Il y a des discussions sur gitHub à ce sujet mais aucune conclusion pour le moment .. Voici le chemin d' Auth0
Dennis Smolek

Merci pour votre réponse. Connaissez-vous de bonnes vidéos pour apprendre l'authService pour angular 2?
AngularM

0

Je le fais comme ça depuis RC 5

this.router.events
  .map( event => event instanceof NavigationStart )
  .subscribe( () => {
    // TODO
  } );

0

Modifiez simplement AppRoutingModule comme

@NgModule({
imports: [RouterModule.forRoot(routes, { scrollPositionRestoration: 'enabled' })],
  exports: [RouterModule]
})

0

Angulaire 8. Vérifiez si l'itinéraire actuel est l'itinéraire de base .

  baseroute: boolean;
  constructor(
    private router: Router,
  ) {
    router.events.subscribe((val: any) => {
      if (val.url == "/") {
        this.baseroute = true;
      } else {
        this.baseroute = false;
      }
    });
  }

0

J'écrirais quelque chose comme ceci:

ngOnInit() {
this.routed = this.router.events.map( event => event instanceof NavigationStart )
  .subscribe(() => {
  } );
}

ngOnDestroy() {
this.routed.unsubscribe();
}

-3

réponse simple pour Angular 8. *

constructor(private route:ActivatedRoute) {
  console.log(route);
}

1
N'est-ce pas exécuté uniquement sur instanciation, ce serait: une seule fois!? Ce n'est pas une solution.
Satria
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.