Como detectar uma alteração de rota no Angular?

428

Eu estou olhando para detectar uma mudança de rota no meu AppComponent.

Posteriormente, irei verificar o token de usuário global para ver se ele está logado. Em seguida, posso redirecionar o usuário se ele não estiver logado.

AngularM
fonte

Respostas:

534

No Angular 2, você pode subscribe(evento Rx) em uma instância do roteador. Então você pode fazer coisas 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*/)
  }
}

Edição 2 (desde 2.0.0)

veja também: Router.events doc

class MyClass {
  constructor(private router: Router) {
    router.events.subscribe((val) => {
        // see also 
        console.log(val instanceof NavigationEnd) 
    });
  }
}
Ludohen
fonte
2
Eu sou capaz de obter dados por event._root.children[0].value._routeConfig.dataEspero que não pode haver melhor maneira
Akshay
6
@Akshay, você viu este artigo de Todd Motto: [Títulos de páginas dinâmicas no Angular 2 com eventos de roteador] ( toddmotto.com/dynamic-page-titles-angular-2-router-events )
Bogac
10
por que dispara 3 vezes?
Toolkit
2
O @Toolkit é porque o evento tem três estados e uma alteração bem-sucedida do URL. Os 3 estados são: 'NavigationStart', 'NavigationEnd' e 'RoutesRecognized'
RicardoGonzales
10
você pode filtrar os eventos facilmente com o filteroperador RxJS . router.events.pipe(filter(e => e instanceof NavigationEnd).subscribe((e) => { ... }
2141818 Simon_Weaver
314

RxJS 6

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

Agradecimentos a Peilonrayz (veja os comentários abaixo)

novo roteador> = 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
  });
}

Você também pode filtrar pelo evento especificado:

import 'rxjs/add/operator/filter';

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

Usar o pairwiseoperador para obter o evento anterior e atual também é uma boa ideia. 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
fonte
1
@GunterZochbauer em vez de 'is' usaria 'instanceof'. E 'event: Event' deve estar entre parênteses. Obrigado por isso, novo e poderoso recurso! Eu gosto disso
Maxim
2
Isso faz jogar um erro de compilação sobre as versões atuaisArgument of type '(event: Event) => void' is not assignable to parameter of type
Rudi Strydom
1
@RudiStrydom & Günter Zöchbauer - o Argument of type '(event: Event) => void' is not assignable to parameter of typeerro ocorre porque, no snippet de filtro, você está inscrevendo-se em um objeto do tipo Event em vez de NavigationEvent.
Bonnici
1
A segunda amostra deve ser NavigationEvent em vez de Event. Também não se esqueça de importação "Evento como NavigationEvent" de @ angular / router
Mick
1
A dica sobre a importação foi para quem quer resolver esse erro :)
Mick
92

Para o Angular 7, alguém deve escrever como:

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


Um exemplo detalhado pode ser o seguinte:

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

   }
}
Сергій Козюк
fonte
2
Isso é ótimo! muito abrangente! funcionou perfeitamente no Angular 7.
MaylorTaylor 01/02/19
1
Normalmente, a navegação em si leva muito pouco tempo se você usar a estratégia de pré-carregamento. Em termos de usabilidade, eu usaria indicadores de carregamento apenas em solicitações http de back-end, se houver.
27419 Phil
5
No construtor , você não deve usar <isto>, seu caso é para ngOnInit.
Sergio Reis
1
Perfeito, como posso obter param.id exato de url?
ShibinRagh 16/05/19
2
A solução não é componente limitado, ele se espalha por todo o aplicativo, consumindo recursos
. Md Rafee
54

Angular 7 , se você quiser subscribepararouter

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
fonte
2
não capturar o evento de redirecionamento
Anandhu Ajayakumar
40

Angular 4.xe acima:

Isso pode ser alcançado usando a propriedade url da classe ActivatedRoute como abaixo,

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

Nota: Você precisa importar e injetar o provedor do angular/routerpacote

import { ActivatedRoute } from '@angular/router`

e

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

O roteador 3.0.0-beta.2 deve ser

this.router.events.subscribe(path => {
  console.log('path = ', path);
});
Mike Lallemont
fonte
que funciona para o caminho atual, mas e o anterior?
Tatsu
16

No angular 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*/
}
)
JBRandri
fonte
3
você perdeu a importação do roteadorimport {Router, NavigationEnd} from "@angular/router"
Damir Beylkhanov
15

As respostas aqui estão corretas router-deprecated. Para a versão mais recente do router:

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

ou

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

Para ver a diferença entre os dois, confira esta pergunta SO .

Editar

Para o mais recente, você deve fazer:

this.router.events.subscribe(event: Event => {
    // Handle route change
});
Dehli
fonte
Fornece algum dado da rota anterior e atual?
akn
O routerfoi atualizado novamente (ainda não atualizei minha resposta), por isso não tenho certeza de como é o último. Pelo routerque escrevi sobre, você não pôde. @akn
Dehli
Você poderia fornecer algum contexto para esta resposta? quais linhas você está substituindo nas outras soluções pelas suas?
Gerard Simpson
12

No Angular 8 você deve fazer como this.router.events.subscribe((event: Event) => {})

Exemplo:

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
fonte
10

No componente, você pode tentar o seguinte:

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
fonte
8

Capturar eventos de alteração de rota da seguinte maneira ...

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
fonte
Perfeito, como posso obter param.id exato de url?
ShibinRagh 16/05/19
6

Localização 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
fonte
Boa resposta. funciona para o meu caso. Obrigado
Umar Tariq
4

acima a maioria das soluções está correta, mas estou enfrentando um problema que é emitido várias vezes 'Navigation emit' event.when eu alterei qualquer rota, esse evento é acionado. Portanto, ouça é a solução completa para o 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
fonte
3

A resposta do @Ludohen é ótima, mas caso você não queira usar, instanceofuse o seguinte

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

dessa maneira, você pode verificar o nome do evento atual como uma sequência e, se o evento ocorreu, pode fazer o que planejou sua função.

Khaled Al-Ansari
fonte
3
por que não usar segurança datilografada?
Pascal
@ Pascal por que o ódio? e o Eventtipo está causando um erro no Atom, é por isso que não o usei
Khaled Al-Ansari
2
@ Pascal não, é um problema angular, pois o evento do roteador não é o mesmo que o evento do navegador e é por isso que o tipo de evento não funciona! eles precisam para criar uma nova interface para este evento, eu deveria ter dito que desde o início mas a votação para baixo razoável não ajudou :)
Khaled Al-Ansari
5
como a minificação é realizada no código de produção, você deve usar instanceOfesse código para que seu exemplo funcione também no código de produção. if(event instanceOf NavigationStart) {
Milan Jaric 31/01
1
Deve serif(event instanceof NavigationStart)
Ketan
1

Estou trabalhando com o aplicativo angular5 e estou enfrentando o mesmo problema. Quando passo pela documentação angular, eles fornecem a melhor solução para lidar com os eventos do roteador. verifique a documentação a seguir.

Representa um evento acionado quando uma navegação termina com êxito

Como usar isso?

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 usar isso?

No meu caso, meu aplicativo compartilha um painel comum para todos os usuários, como usuários, Admins, mas preciso mostrar e ocultar algumas opções da barra de navegação conforme os tipos de usuário.

É por isso que sempre que a URL muda, eu preciso chamar o método de serviço, que retorna as informações do usuário logadas conforme a resposta, irei para outras operações.

Bhagvat Lande
fonte
0

O seguinte TIPO de obras e pode ser complicado para você.

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

Infelizmente, isso redireciona mais tarde no processo de roteamento do que eu gostaria. O onActivate()componente de destino original é chamado antes do redirecionamento.

Existe um @CanActivatedecorador que você pode usar no componente de destino, mas este é a) não centralizado eb) não se beneficia dos serviços injetados.

Seria ótimo se alguém pudesse sugerir uma maneira melhor de autorizar centralmente uma rota antes que ela seja confirmada. Tenho certeza de que deve haver uma maneira melhor.

Este é o meu código atual (como eu o alteraria para ouvir a alteração de rota?):

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
fonte
Eu já vi pessoas estenderem o routerOutlet para adicionar seu código de autenticação, que é uma maneira. Fala-se no GitHub sobre isso, mas nenhuma conclusão ainda .. Aqui está o caminho de Auth0: auth0.com/blog/2015/05/14/...
Dennis Smolek
Obrigado pela sua resposta. Você conhece algum vídeo bom para aprender o authService para angular 2?
AngularM
0

Eu faço assim desde RC 5

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

Basta fazer alterações no AppRoutingModule como

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

Angular 8. Verifique se a rota atual é a rota base .

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

Eu escreveria algo assim:

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

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

resposta simples para o Angular 8. *

constructor(private route:ActivatedRoute) {
  console.log(route);
}
er-Chaan
fonte
1
Isso não é executado apenas na instanciação, seria: apenas uma vez !? Esta não é uma solução.
Satria