Come rilevare un cambio di rotta in Angolare?


428

Sto cercando di rilevare un cambio di rotta nel mio AppComponent.

Successivamente verificherò il token utente globale per vedere se è connesso. Quindi posso reindirizzare l'utente se non ha effettuato l'accesso.

Risposte:


534

In Angular 2 è possibile subscribe(evento Rx) a un'istanza del router. Quindi puoi fare cose del genere

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

Modifica (dal rc.1)

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

Modifica 2 (dalla 2.0.0)

vedi anche: Router.events doc

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

2
Sono in grado di ottenere dati event._root.children[0].value._routeConfig.datasperando che ci possa essere un modo migliore
Akshay,

6
@Akshay hai visto questo articolo di Todd Motto: [Titoli di pagina dinamici in Angular 2 con eventi router] ( toddmotto.com/dynamic-page-titles-angular-2-router-events )
Bogac

10
perché spara 3 volte?
Toolkit,

2
@Toolkit è perché l'evento ha 3 stati e in caso di successo cambio di url. I 3 stati sono: 'NavigationStart', 'NavigationEnd' e 'RouteRecognized'
RicardoGonzales,

10
puoi filtrare facilmente gli eventi con l' filteroperatore RxJS . router.events.pipe(filter(e => e instanceof NavigationEnd).subscribe((e) => { ... }
Simon_Weaver il

314

RxJS 6

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

Grazie a Peilonrayz (vedi commenti sotto)

nuovo router> = 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
  });
}

Puoi anche filtrare per evento dato:

import 'rxjs/add/operator/filter';

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

Anche usare l' pairwiseoperatore per ottenere l'evento precedente e attuale è una buona idea. 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 invece di 'is' Vorrei usare 'instanceof'. E 'event: Event' dovrebbe essere tra parentesi. Grazie per questa nuova funzionalità piuttosto potente! Mi piace
Maxim

2
Questo genera un errore di compilazione sulle versioni correntiArgument 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 typeerrore è dovuto al fatto che nel tuo frammento di filtro stai sottoscrivendo un oggetto di tipo Event anziché NavigationEvent.
Bonnici,

1
Il secondo esempio dovrebbe essere NavigationEvent anziché Event. Inoltre, non dimenticare di importare "Event as NavigationEvent" da @ angular / router
Mick,

1
Il suggerimento sull'importazione era per chiunque cercasse di risolvere questo errore :)
Mick,

92

Per Angular 7 qualcuno dovrebbe scrivere come:

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


Un esempio dettagliato può essere il seguente:

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
Questo è fantastico! molto completo! ha funzionato perfettamente su Angular 7.
MaylorTaylor

1
Di solito la navigazione stessa richiede pochissimo tempo se si utilizza la strategia di precaricamento. In termini di usabilità, utilizzerei gli indicatori di caricamento solo su richieste http back-end, se non del tutto.
Phil

5
Nel costruttore , non dovresti usare <questo>, il tuo caso è per ngOnInit.
Sergio Reis,

1
Perfetto, come posso ottenere il parametro esatto di url?
ShibinRagh,

2
La soluzione non è limitata ai componenti, si diffonde in tutta l'app, consumando risorse
Md. Rafee,

54

Angolare 7 , se si vuole subscribearouter

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
non cattura l'evento di reindirizzamento
Anandhu Ajayakumar il

40

Angolare 4.xe versioni successive:

Ciò può essere ottenuto utilizzando la proprietà url della classe ActivatedRoute come di seguito,

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

Nota: è necessario importare e iniettare il provider dal angular/routerpacchetto

import { ActivatedRoute } from '@angular/router`

e

constructor(private activatedRoute : ActivatedRoute){  }

18

Il router 3.0.0-beta.2 dovrebbe essere

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

che funziona per il percorso corrente, ma per quanto riguarda il precedente?
Tatsu,

16

In angolare 6 e 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
hai perso l'importazione per Routerimport {Router, NavigationEnd} from "@angular/router"
Damir Beylkhanov il

15

Le risposte qui sono corrette per router-deprecated. Per l'ultima versione di router:

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

o

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

Per vedere la differenza tra i due, controlla questa domanda SO .

modificare

Per l'ultimo devi fare:

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

Fornisce dati sul percorso precedente e corrente?
Akn

È routerstato aggiornato di nuovo (non ho ancora aggiornato la mia risposta), quindi non sono sicuro di come sia l'ultima. Per quello di cui routerho scritto, non puoi. @akn
Dehli,

Per favore, potresti fornire un contesto per questa risposta? quali linee stai sostituendo nelle altre soluzioni con le tue?
Gerard Simpson

12

In Angular 8 dovresti fare come this.router.events.subscribe((event: Event) => {})

Esempio:

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

Nel componente, potresti provare questo:

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

Cattura gli eventi di cambio di rotta nel modo seguente ...

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

Perfetto, come posso ottenere il parametro esatto di url?
ShibinRagh,

6

La posizione funziona ...

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

Bella risposta. funziona per il mio caso. Grazie
Umar Tariq,

4

sopra la maggior parte delle soluzioni corrette, ma sto affrontando questo problema emettere più volte l'evento "Emissione navigazione". Quando stavo cambiando qualsiasi rotta questo evento viene attivato. Quindi ascoltare è la soluzione completa per 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

La risposta di @Ludohen è ottima, ma nel caso in cui non si desideri utilizzare instanceofutilizzare quanto segue

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

in questo modo puoi controllare il nome dell'evento corrente come una stringa e se l'evento si è verificato puoi fare ciò che hai pianificato per la tua funzione.


3
perché non usare la sicurezza dattiloscritta?
Pascal,

@Pascal perché l'odio? e il Eventtipo sta causando un errore in Atom ed è per questo che non l'ho usato
Khaled Al-Ansari

2
@Pascal no è un problema angolare poiché l'evento del router non è lo stesso dell'evento del browser ed è per questo che il tipo di evento non funzionerà! hanno bisogno di creare una nuova interfaccia per questo evento, avrei dovuto dirlo dall'inizio, ma l'irragionevole voto negativo non ha aiutato :)
Khaled Al-Ansari,

5
poiché la minificazione viene eseguita nel codice di produzione, è preferibile utilizzarla in instanceOfmodo che anche il tuo esempio funzioni nel codice di produzione. if(event instanceOf NavigationStart) {
Milan Jaric,

1
Dovrebbe essereif(event instanceof NavigationStart)
Ketan il

1

Sto lavorando con l'applicazione angular5 e sto affrontando lo stesso problema. quando passo attraverso la documentazione angolare forniscono la migliore soluzione per la gestione degli eventi del router. Controlla la seguente documentazione.

Rappresenta un evento attivato quando una navigazione termina correttamente

Come usare questo?

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

Quando usarlo?

Nel mio caso la mia applicazione condivide dashboard comune per tutti gli utenti come utenti, amministratori, ma devo mostrare e nascondere alcune opzioni della barra di navigazione in base ai tipi di utenti.

Ecco perché ogni volta che l'URL cambia, devo chiamare il metodo di servizio che restituisce le informazioni dell'utente registrato come da risposta, andrò per ulteriori operazioni.


0

Il seguente tipo di opere e può fare il difficile per te.

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

Purtroppo questo reindirizza più tardi nel processo di routing di quanto vorrei. IlonActivate() componente di destinazione originale viene chiamato prima del reindirizzamento.

C'è un @CanActivate decoratore che puoi usare sul componente target ma questo è a) non centralizzato eb) non beneficia dei servizi iniettati.

Sarebbe bello se qualcuno potesse suggerire un modo migliore di autorizzare centralmente un percorso prima che venga eseguito. Sono sicuro che ci deve essere un modo migliore.

Questo è il mio codice attuale (come lo cambierei per ascoltare il cambio di rotta?):

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

Ho visto gente estendere routerOutlet per aggiungere il loro codice di autenticazione che è un modo. Ne parliamo su gitHub, ma ancora nessuna conclusione .. Ecco la via di Auth0
Dennis Smolek,

Grazie per la vostra risposta. Conosci qualche buon video per l'apprendimento di authService per angular 2?
AngularM,

0

Lo faccio così da RC 5

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

0

Apporta semplicemente modifiche su AppRoutingModule come

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

0

Angolare 8. Controllare se il percorso corrente è il percorso di base .

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

0

Scriverei qualcosa del genere:

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

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

-3

risposta semplice per Angular 8. *

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

1
Questo non viene eseguito solo all'istanza, sarebbe: solo una volta !? Questa non è una soluzione.
Satria,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.