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.