¿Cómo detectar un cambio de ruta en Angular?

428

Estoy buscando detectar un cambio de ruta en mi AppComponent.

Posteriormente, comprobaré el token de usuario global para ver si ha iniciado sesión. Luego, puedo redirigir al usuario si no ha iniciado sesión.

AngularM
fuente

Respuestas:

534

En Angular 2 puede subscribe(evento Rx) a una instancia de enrutador. Entonces puedes hacer cosas como

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

Editar (desde rc.1)

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

Edición 2 (desde 2.0.0)

ver también: Router.events doc

class MyClass {
  constructor(private router: Router) {
    router.events.subscribe((val) => {
        // see also 
        console.log(val instanceof NavigationEnd) 
    });
  }
}
Ludohen
fuente
2
Puedo obtener datos event._root.children[0].value._routeConfig.data, espero que haya una mejor manera
Akshay
66
@Akshay, ¿has visto este artículo de Todd Motto: [títulos de página dinámicos en Angular 2 con eventos de enrutador] ( toddmotto.com/dynamic-page-titles-angular-2-router-events )
Bogac
10
¿Por qué dispara 3 veces?
Kit de herramientas del
2
@Toolkit es porque el evento tiene 3 estados y en un cambio exitoso de url. Los 3 estados son: 'NavigationStart', 'NavigationEnd' y 'RoutesRecognized'
RicardoGonzales
10
Puede filtrar los eventos fácilmente con el filteroperador RxJS . router.events.pipe(filter(e => e instanceof NavigationEnd).subscribe((e) => { ... }
Simon_Weaver
314

RxJS 6

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

Gracias a Peilonrayz (ver comentarios a continuación)

nuevo enrutador> = 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
  });
}

También puede filtrar por evento dado:

import 'rxjs/add/operator/filter';

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

Usar el pairwiseoperador para obtener el evento anterior y actual también es una buena 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);
        });
    };
}
Günter Zöchbauer
fuente
1
@GunterZochbauer en lugar de 'es', usaría 'instanceof'. Y 'evento: evento' debe estar entre paréntesis. ¡Gracias por esto, nueva característica bastante poderosa! Me gusta
Maxim
2
Esto arroja un error de compilación en las versiones actualesArgument of type '(event: Event) => void' is not assignable to parameter of type
Rudi Strydom
1
@RudiStrydom & Günter Zöchbauer: el Argument of type '(event: Event) => void' is not assignable to parameter of typeerror se debe a que en el fragmento de filtro se está suscribiendo a un objeto de tipo Evento en lugar de NavigationEvent.
Bonnici
1
La segunda muestra debe ser NavigationEvent en lugar de Event. Además, no olvide importar "Evento como evento de navegación" desde @ angular / router
Mick
1
La sugerencia sobre la importación fue para cualquiera que quiera resolver este error :)
Mick
92

Para Angular 7, alguien debería escribir como:

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


Un ejemplo detallado puede ser el siguiente:

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

   }
}
Сергій Козюк
fuente
2
¡Esto es genial! muy completo! funcionó perfectamente en Angular 7.
MaylorTaylor
1
Por lo general, la navegación en sí misma toma muy poco tiempo si utiliza la estrategia de precarga. En términos de usabilidad, usaría indicadores de carga solo en solicitudes http de backend, si es que lo hace.
Phil
55
En el constructor , no debe usar <this>, su caso es para ngOnInit.
Sergio Reis
1
Perfecto, ¿cómo puedo obtener el parámetro exacto de url?
ShibinRagh
2
La solución no es componente limitada, se extiende a lo largo de la aplicación, los recursos que consumen
Md. Rafee
54

Angular 7 , si quieres 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);
  });
}
Druta Ruslan
fuente
2
no captura el evento de redirección
Anandhu Ajayakumar
40

Angular 4.xy superior:

Esto se puede lograr usando la propiedad url de la clase ActivatedRoute como se muestra a continuación,

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

Nota: que necesita importar e inyectar el proveedor del angular/routerpaquete

import { ActivatedRoute } from '@angular/router`

y

constructor(private activatedRoute : ActivatedRoute){  }
Aravind
fuente
18

Router 3.0.0-beta.2 debería ser

this.router.events.subscribe(path => {
  console.log('path = ', path);
});
Mike Lallemont
fuente
eso funciona para la ruta actual, pero ¿qué pasa con la anterior?
tatsu
16

En angular 6 y 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*/
}
)
JBRandri
fuente
3
te perdiste la importación para Routerimport {Router, NavigationEnd} from "@angular/router"
Damir Beylkhanov
15

Las respuestas aquí son correctas router-deprecated. Para la última versión de router:

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

o

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

Para ver la diferencia entre los dos, consulte esta pregunta SO .

Editar

Para lo último debes hacer:

this.router.events.subscribe(event: Event => {
    // Handle route change
});
Dehli
fuente
¿Proporciona datos de la ruta anterior y actual?
akn
Se routerha actualizado nuevamente (aún no he actualizado mi respuesta), por lo que no estoy seguro de cómo es para la última. Por lo routerque escribí, no pudiste. @akn
Dehli
Por favor, ¿podría proporcionar algún contexto para esta respuesta? ¿Qué líneas estás reemplazando en las otras soluciones con las tuyas?
Gerard Simpson el
12

En Angular 8 deberías hacer como this.router.events.subscribe((event: Event) => {})

Ejemplo:

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
            }
        });
   }
}
Rohit.007
fuente
10

En el componente, es posible que desee probar esto:

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
            }
        });
}
mojtaba ramezani
fuente
8

Capture eventos de cambio de ruta de la siguiente manera ...

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;
                }
                }
            }
        });
    }
}
Narottam Goyal
fuente
Perfecto, ¿cómo puedo obtener el parámetro exacto de url?
ShibinRagh
6

La ubicación funciona ...

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);
    }
}
webmaster_sean
fuente
Buena respuesta. Funciona para mi caso. Gracias
Umar Tariq
4

arriba, la mayoría de las soluciones son correctas, pero estoy enfrentando un problema que emite varias veces el evento 'Navegación emitida'. Cuando cambié alguna ruta, este evento se activa. Entonces escuchar es la solución completa para 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();
  }
} 
yala ramesh
fuente
3

La respuesta de @Ludohen es excelente, pero en caso de que no quiera usarla, instanceofuse lo siguiente

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

de esta manera, puede verificar el nombre del evento actual como una cadena y, si ocurrió el evento, puede hacer lo que planeó que hiciera su función.

Khaled Al-Ansari
fuente
3
¿Por qué no usar seguridad mecanografiada?
Pascal
@Pascal ¿por qué el odio? y el Eventtipo está causando un error en Atom por eso no lo usé
Khaled Al-Ansari
2
@Pascal no, es un problema angular ya que el evento del enrutador no es el mismo que el evento del navegador y es por eso que el tipo de evento no funcionará. necesitan crear una nueva interfaz para este evento, debería haberlo dicho desde el principio, pero el voto irracional irrazonable no ayudó :)
Khaled Al-Ansari
55
dado que la minificación se realiza en el código de producción, debería usarla instanceOfpara que su ejemplo también funcione en el código de producción. if(event instanceOf NavigationStart) {
Milan Jaric
1
Debería serif(event instanceof NavigationStart)
Ketan
1

Estoy trabajando con la aplicación angular5 y estoy enfrentando el mismo problema. Cuando reviso la documentación angular, proporcionan la mejor solución para manejar los eventos del enrutador. Verifique la siguiente documentación.

Representa un evento desencadenado cuando una navegación finaliza correctamente

Como usar esto ?

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

¿Cuándo usar esto?

En mi caso, mi aplicación comparte un tablero común para todos los usuarios, como usuarios, administradores, pero necesito mostrar y ocultar algunas opciones de la barra de navegación según los tipos de usuarios.

Es por eso que cada vez que cambia la URL necesito llamar al método de servicio que devuelve la información de usuario registrada según la respuesta, iré para más operaciones.

Bhagvat Lande
fuente
0

El siguiente TIPO de trabajos y puede hacer el truco para usted.

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

Desafortunadamente, esto redirige más tarde en el proceso de enrutamiento de lo que me gustaría. losonActivate() del componente de destino original se llama antes de la redirección.

Hay un @CanActivate decorador que puede usar en el componente de destino, pero este es a) no centralizado yb) no se beneficia de los servicios inyectados.

Sería genial si alguien puede sugerir una mejor manera de autorizar centralmente una ruta antes de que se comprometa. Estoy seguro de que debe haber una mejor manera.

Este es mi código actual (¿Cómo lo cambiaría para escuchar el cambio de ruta?):

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: '/'})]);
Richard Evans
fuente
He visto a personas extender el routerOutlet para agregar su código de autenticación, que es de una manera. Se habla en gitHub al respecto, pero aún no hay conclusión. Aquí está el camino de Auth0
Dennis Smolek
Gracias por su respuesta. ¿Conoces algún buen video para aprender el authService para angular 2?
AngularM
0

Lo hago así desde RC 5

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

Simplemente haga cambios en AppRoutingModule como

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

Angular 8. Compruebe si la ruta actual es la ruta base .

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

Escribiría algo como esto:

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

ngOnDestroy() {
this.routed.unsubscribe();
}
Lalith-AIS
fuente
-3

respuesta simple para Angular 8. *

constructor(private route:ActivatedRoute) {
  console.log(route);
}
er-Chaan
fuente
1
¿No se ejecuta esto solo en instanciación, eso sería: solo una vez? Esta no es una solución.
Satria