Como determinar o URL da página anterior no Angular?


104

Suponha que eu esteja atualmente na página que contém o URL /user/:id. Agora, a partir desta página, navego para a próxima página :id/posts.

Agora existe uma maneira, para que eu possa verificar qual é a URL anterior, ou seja /user/:id.

Abaixo estão minhas rotas

export const routes: Routes = [
  { 
    path: 'user/:id', component: UserProfileComponent
  },
  {  
    path: ':id/posts', component: UserPostsComponet 
  }
];

Respostas:


83

Você pode se inscrever para mudanças de rota e armazenar o evento atual para que possa usá-lo quando o próximo acontecer

previousUrl: string;
constructor(router: Router) {
  router.events
  .pipe(filter(event => event instanceof NavigationEnd))
  .subscribe((event: NavigationEnd) => {
    console.log('prev:', event.url);
    this.previousUrl = event.url;
  });
}

Veja também Como detectar uma mudança de rota no Angular?


12
Obrigado @ Günter Você sempre salva meu dia.
Chandra Shekhar

34
Isso não lista a rota anterior para mim, apenas a rota atual.
David Aguirre

2
Depende do que você espera. A primeira vez é nullporque não existe uma rota anterior. Você também precisa fazer isso no roteador raiz, caso contrário, você só obterá quando navegar entre as rotas secundárias deste componente.
Günter Zöchbauer

8
Isso não fornece o url anterior quando o construtor é executado pela primeira vez.
Ekaitz Hernandez Troyas

9
Qual valor você espera como url anterior quando o construtor é executado pela primeira vez?
Günter Zöchbauer

114

Talvez todas as outras respostas sejam para angular 2.X.

Agora não funciona para o angular 5.X. Estou trabalhando nisso.

com apenas NavigationEnd, você não pode obter url anterior.

porque o roteador funciona de "NavigationStart", "RoutesRecognized", ..., para "NavigationEnd".

Você pode verificar com

    router.events.forEach((event) => {
  console.log(event);
});

Mas ainda assim você não pode obter url anterior, mesmo com "NavigationStart".

Agora você precisa usar os pares.

import 'rxjs/add/operator/filter';
import 'rxjs/add/operator/pairwise';

constructor(private router: Router) {
  this.router.events
    .filter(e => e instanceof RoutesRecognized)
    .pairwise()
    .subscribe((event: any[]) => {
      console.log(event[0].urlAfterRedirects);
    });
}
    

Com o emparelhamento, você pode ver de e para onde é o url.

"RoutesRecognized" é a etapa de mudança da origem para o URL de destino.

então filtre-o e obtenha o URL anterior dele.

Por último mas não menos importante,

coloque este código no componente pai ou superior (por exemplo, app.component.ts)

porque este código dispara após terminar o roteamento.

Atualizar angular 6+

O events.filterdá erro porque o filtro não faz parte dos eventos, então mude o código para

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

this.router.events
.pipe(filter((evt: any) => evt instanceof RoutesRecognized), pairwise())
.subscribe((events: RoutesRecognized[]) => {
  console.log('previous url', events[0].urlAfterRedirects);
  console.log('current url', events[1].urlAfterRedirects);
});

2
Implementado como um serviço e funciona muito bem. Estou usando o angular 6.1.7.
A. El Idrissi

5
@ tjvg1991 Atualizar página significa que você perdeu dados da memória. se você mantiver os dados anteriores, precisará usar localStorage ou cookie. (salvar dados no seu local, não na memória)
BYUNGJU JIN

@BYUNGJUJIN Obrigado por isso!
john

1
@BYUNGJUIN armazena o valor de retorno de subscribe()em um campo e o chama unsubscribe()em ngOnDestroy(). Deve haver severs. perguntas com exemplos sobre cancelamento de inscrição aqui no SO.
Günter Zöchbauer

1
@app, verifique isto para cancelar a inscrição: malcontentboffin.com/2017/12/…
BYUNGJU JIN

51

Crie um serviço injetável:

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

 /** A router wrapper, adding extra functions. */
@Injectable()
export class RouterExtService {

  private previousUrl: string = undefined;
  private currentUrl: string = undefined;

  constructor(private router : Router) {
    this.currentUrl = this.router.url;
    router.events.subscribe(event => {
      if (event instanceof NavigationEnd) {        
        this.previousUrl = this.currentUrl;
        this.currentUrl = event.url;
      };
    });
  }

  public getPreviousUrl(){
    return this.previousUrl;
  }    
}

Em seguida, use-o onde você precisar. Para armazenar a variável atual o mais rápido possível, é necessário usar o serviço no AppModule.

// AppModule
export class AppModule {
  constructor(private routerExtService: RouterExtService){}

  //...

}

// Using in SomeComponent
export class SomeComponent implements OnInit {

  constructor(private routerExtService: RouterExtService, private location: Location) { } 

  public back(): void {
    this.location.back();
  }

  //Strange name, but it makes sense. Behind the scenes, we are pushing to history the previous url
  public goToPrevious(): void {
    let previous = this.routerExtService.getPreviousUrl();

    if(previous)
      this.routerExtService.router.navigateByUrl(previous);
  }

  //...

}

2
Acho que essa é a solução mais elegante. Tente mesclar este código com o novo filtro e solução de pares de: stackoverflow.com/a/35287471/518879
danger89

2
Ps. Não se esqueça de adicionar este RouterExtService ao apps-routing.module.ts (no meu caso), assim:@NgModule({ ..., providers: [RouterExtService]}) export class AppRoutingModule { }
danger89

Ok, há um grande problema com essa solução de serviço .. No meu caso chamo o routerExtService.getPreviousUrl()método no construtor de um serviço usado em um componente. Por algum motivo, ele é chamado antes da atualização real. O que significa que temos uma dependência de tempo! Eu acho que é muito mais fácil usar o Assunto.
hazard89,

Bem, funcionou bem para mim em um pequeno projeto. Talvez precise de alguns ajustes para atender às suas necessidades. Você resolveu o problema?
Juliano

Atualmente estou usando os chamados parâmetros de matriz de URL para 'armazenar' meu estado em minha URL. Por padrão, a URL do navegador armazena o estado agora ao usar o botão Voltar. let params = new HttpParams({fromString: retrieveURL}).set('name', 'victor') const paramsObject = params.keys().reduce((obj, key) => { obj[key] = params.get(key) return obj }, {}) this.router.navigate([paramsObject], { relativeTo: this.route })
hazard89

20

Código atualizado do Angular 6 para obter o URL anterior como string.

import { Router, RoutesRecognized } from '@angular/router';
import { filter, pairwise } from 'rxjs/operators';


export class AppComponent implements OnInit {

    constructor (
        public router: Router
    ) {
    }

    ngOnInit() {
        this.router.events
            .pipe(filter((e: any) => e instanceof RoutesRecognized),
                pairwise()
            ).subscribe((e: any) => {
                console.log(e[0].urlAfterRedirects); // previous url
            });
    }

Isso retorna a url que foi bloqueada por um guarda, existe uma maneira de obter apenas a url anterior que foi ativada (não bloqueada pelo guarda)?
Exocomp de

1
Alguma dica sobre a melhor maneira de cancelar a assinatura do roteador?
j4v1

Trabalho! Eu realmente não sei por que "NavigationEnd" não funciona
davidwillianx

13

Isso funcionou para mim nas versões angular> = 6.x:

this.router.events
            .subscribe((event) => {
              if (event instanceof NavigationStart) {
                window.localStorage.setItem('previousUrl', this.router.url);
              }
            });

11

Estou usando o Angular 8 e a resposta de @franklin-pious resolve o problema. No meu caso, obter o url anterior dentro de uma assinatura causa alguns efeitos colaterais se estiver anexado a alguns dados na visualização.

A solução alternativa que usei foi enviar o url anterior como um parâmetro opcional na navegação da rota.

this.router.navigate(['/my-previous-route', {previousUrl: 'my-current-route'}])

E para obter esse valor no componente:

this.route.snapshot.paramMap.get('previousUrl')

this.router e this.route são injetados dentro do construtor de cada componente e são importados como membros @ angular / router.

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

10

Angular 8 e rxjs 6 na versão 2019

Eu gostaria de compartilhar a solução com base em outras grandes soluções.

Primeiro, faça um serviço para escutar as alterações de rotas e salve a última rota anterior em um Assunto de comportamento, em seguida, forneça este serviço no app.component principal no construtor e use este serviço para obter a rota anterior que você deseja, sempre que quiser.

Caso de uso: você deseja redirecionar o usuário para uma página de anúncio e, em seguida, redirecioná-lo automaticamente para o lugar de onde ele veio, então você precisa da última rota anterior para fazer isso.

// service : route-events.service.ts

import { Injectable } from '@angular/core';
import { Router, RoutesRecognized } from '@angular/router';
import { BehaviorSubject } from 'rxjs';
import { filter, pairwise } from 'rxjs/operators';
import { Location } from '@angular/common';

@Injectable()
export class RouteEventsService {

    // save the previous route
  public previousRoutePath = new BehaviorSubject<string>('');

  constructor(
    private router: Router,
    private location: Location
  ) {

    // ..initial prvious route will be the current path for now
    this.previousRoutePath.next(this.location.path());


    // on every route change take the two events of two routes changed(using pairwise)
    // and save the old one in a behavious subject to access it in another component
    // we can use if another component like intro-advertise need the previous route
    // because he need to redirect the user to where he did came from.
    this.router.events.pipe(
      filter(e => e instanceof RoutesRecognized),
      pairwise(),
        )
    .subscribe((event: any[]) => {
        this.previousRoutePath.next(event[0].urlAfterRedirects);
    });

  }
}

fornecer o serviço em app.module

  providers: [
    ....
    RouteEventsService,
    ....
  ]

Injete-o em app.component

  constructor(
    private routeEventsService: RouteEventsService
  )

finalmente use a rota anterior salva no componente que você deseja

  onSkipHandler(){
    // navigate the user to where he did came from
    this.router.navigate([this.routeEventsService.previousRoutePath.value]);
  }

Isso funciona muito bem. Mas eu tenho uma pergunta rápida. Você sempre cancela a inscrição?
w0ns88

adicionar take (1) assim -> emparelhados (), take (1)). subscrever ((e: qualquer)
Mukus

1
Observe que se você usar @Injectable({ providedIn: 'root' })o serviço é carregado automaticamente no módulo raiz (AppModule) do projeto e, portanto, você não precisa fornecê-lo manualmente ao app.module. Veja a documentação para detalhes. Não é necessário cancelar a assinatura do roteador Observáveis ​​conforme declarado nesta resposta
Hkidd

7

PARA ANGULAR 7+

Na verdade, desde o Angular 7.2 não há necessidade de usar um serviço para salvar a url anterior. Você poderia apenas usar o objeto de estado para definir o último url antes de vincular à página de login. Aqui está um exemplo de um cenário de login.

@Component({ ... })
class SomePageComponent {
  constructor(private router: Router) {}

  checkLogin() {
    if (!this.auth.loggedIn()) {
      this.router.navigate(['login'], { state: { redirect: this.router.url } });
    }
  }
}
@Component({...})
class LoginComponent {
  constructor(private router: Router) {}

  backToPreviousPage() {
    const { redirect } = window.history.state;

    this.router.navigateByUrl(redirect || '/homepage');
  }
}
----------------

Além disso, você também pode passar os dados no modelo:

@Component({
  template: '<a routerLink="/some-route" [state]="{ redirect: router.url}">Go to some route</a>'
})
class SomePageComponent {
  constructor(public router: Router) {}
}

3

@ GünterZöchbauer também você pode salvá-lo no armazenamento local, mas eu não prefiro) melhor salvar no serviço e obter esse valor de lá

 constructor(
        private router: Router
      ) {
        this.router.events
          .subscribe((event) => {
            if (event instanceof NavigationEnd) {
              localStorage.setItem('previousUrl', event.url);
            }
          });
      }

3

Você pode usar o Local conforme mencionado aqui .

Este é o meu código se o link abrir em uma nova guia

navBack() {
    let cur_path = this.location.path();
    this.location.back();
    if (cur_path === this.location.path())
     this.router.navigate(['/default-route']);    
  }

Importações necessárias

import { Router } from '@angular/router';
import { Location } from '@angular/common';

0

Bastante simples usando previousNavigationobjeto:

this.router.events
  .pipe(
    filter(e => e instanceof NavigationEnd && this.router.getCurrentNavigation().previousNavigation),
    map(() => this.router.getCurrentNavigation().previousNavigation.finalUrl.toString()),
  )
  .subscribe(previousUrl => {}); 

0

Tive dificuldade para acessar o url anterior dentro de um guarda.
Sem implementar uma solução personalizada, esta está funcionando para mim.

public constructor(private readonly router: Router) {
};

public ngOnInit() {
   this.router.getCurrentNavigation().previousNavigation.initialUrl.toString();
}

O url inicial será a página de url anterior.


0

Esta solução simples funcionou para mim.

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

export class TempComponent {
    constructor(private router: Router) {
        this.router.events.pairwise().subscribe((event) => {
            console.log(event); // NavigationEnd will have last and current visit url
        });
    };
}


-2

Usando pairwise do rxjx, você pode conseguir isso mais facilmente. importar {filtro, par a par} de 'rxjs / operadores';

previousUrl: string;
constructor(router: Router) {
router.events
  .pipe(filter((evt: any) => evt instanceof RoutesRecognized), pairwise())
  .subscribe((events: RoutesRecognized[]) => {
  console.log('previous url', events[0].urlAfterRedirects);
  console.log('current url', events[1].urlAfterRedirects);
  this.previousUrl = events[0].urlAfterRedirects;
});

}


-6

Tive um problema semelhante quando quis voltar à página anterior. A solução foi mais fácil do que eu poderia imaginar.

<button [routerLink]="['../']">
   Back
</button>

E ele retorna ao url pai. Espero que ajude alguém;)


Isso não vai funcionar, você está dizendo para subir no caminho do roteador, ao invés do url anterior, como o OP declarou.
Frederic Yesid Peña Sánchez,

Isso não funciona se o seu url for complexo com parâmetros ou não tiver o mesmo caminho do pai. Funciona apenas se você quiser voltar de "algo / pai / filho" para "algo / pai".
A. El Idrissi
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.