¿Cómo determinar la URL de la página anterior en Angular?

100

Supongamos que estoy en la página que tiene la URL /user/:id. Ahora desde esta página navego a la página siguiente :id/posts.

Ahora, ¿hay alguna manera de que pueda verificar cuál es la URL anterior /user/:id?

Abajo están mis rutas

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

Respuestas:

80

Puede suscribirse a los cambios de ruta y almacenar el evento actual para poder usarlo cuando ocurra el próximo

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

Consulte también ¿Cómo detectar un cambio de ruta en Angular?

Günter Zöchbauer
fuente
12
Gracias @ Günter Tú siempre me salvas el día.
Chandra Shekhar
29
Esto no muestra la ruta anterior para mí, solo la ruta actual.
David Aguirre
2
Depende de lo que esperas. La primera vez es nullporque no hay ruta previa. También debe hacer esto en el enrutador raíz; de lo contrario, solo lo obtendrá cuando navegue entre las rutas secundarias de este componente.
Günter Zöchbauer
8
Esto no da una URL anterior cuando el constructor se ejecuta por primera vez.
Ekaitz Hernandez Troyas
9
¿Qué valor espera como url anterior cuando el constructor se ejecuta por primera vez?
Günter Zöchbauer
109

Quizás todas las demás respuestas sean para 2.X angular.

Ahora no funciona para angular 5.X. Estoy trabajando con eso.

solo con NavigationEnd, no puede obtener la URL anterior.

porque el enrutador funciona desde "NavigationStart", "RoutesRecognized", ..., hasta "NavigationEnd".

Puedes consultar con

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

Pero aún no puede obtener la URL anterior incluso con "NavigationStart".

Ahora necesitas usar por 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);
    });
}
    

Con pares, puede ver de qué URL es y hacia.

"RoutesRecognized" es el paso de cambio de la URL de origen a la de destino.

así que fíltralo y obtén la URL anterior.

Por último, si bien no menos importante,

poner este código en el componente principal o superior (por ejemplo, app.component.ts)

porque este código se activa después de terminar el enrutamiento.

Actualizar angular 6+

El events.filterda error porque el filtro no es parte de los eventos, así que cambie el código a

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);
});
BYUNGJU JIN
fuente
2
Implementado como un servicio y funciona muy bien. Estoy usando angular 6.1.7.
A. El Idrissi
5
@ tjvg1991 Actualizar página significa que perdió datos de memoria. si conserva los datos anteriores, debe utilizar localStorage o cookie. (guarde los datos en su memoria local no)
BYUNGJU JIN
Solo quiero eliminar el botón Upvote Gracias, hombre.
Muhammad Umair
@BYUNGJUJIN ¡Gracias por esto!
Juan
@ BYUNGJU JIN gracias trabaja para mí. ¿Cómo obtengo el valor de param del enlace de redireccionamiento? Por ejemplo, events [0] .urlAfterRedirects me da '/ InventoryDetails; test = 0; id = 45', quiero obtener el valor de id de esto. ¿Cómo puedo hacerlo sin usar subString.
JNPW
49

Crea un servicio inyectable:

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

Luego utilícelo en todos los lugares que necesite. Para almacenar la variable actual lo antes posible, es necesario utilizar el servicio en 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);
  }

  //...

}
Juliano
fuente
2
Creo que esta es la solución más elegante. Intente fusionar este código con el nuevo filtro y la solución por pares de: stackoverflow.com/a/35287471/518879
danger89
2
PD. No olvide agregar este RouterExtService a las aplicaciones-routing.module.ts (en mi caso), así:@NgModule({ ..., providers: [RouterExtService]}) export class AppRoutingModule { }
danger89
De acuerdo, hay un gran problema con esta solución de servicio. En mi caso, llamo al routerExtService.getPreviousUrl()método en el constructor de un servicio usado en un componente. Por alguna razón, esto se llamó antes de la actualización real. ¡Lo que significa que tenemos una dependencia del tiempo! Creo que es mucho más fácil de usar Subject.
danger89
Bueno, funcionó bien para mí en un proyecto pequeño. Tal vez necesite algunos ajustes para satisfacer sus necesidades. ¿Has resuelto el problema?
Juliano
Actualmente estoy usando los llamados parámetros URL Matrix para 'almacenar' mi estado en mi URL, de forma predeterminada, la URL del navegador almacena el estado ahora cuando se usa el botón Atrás. 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 })
danger89
20

Código actualizado de Angular 6 para obtener la URL anterior como cadena.

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
            });
    }
Franklin Piadoso
fuente
Esto devuelve la URL que ha sido bloqueada por un guardia, ¿hay alguna manera de obtener solo la URL anterior que ha sido activada (no bloqueada por el guardia)?
Exocomp
1
¿Alguna pista sobre la mejor manera de darse de baja del enrutador observable?
j4v1
¡Trabajos! No se sabe muy bien por qué "NavigationEnd" no funciona
davidwillianx
13

Esto funcionó para mí en versiones angulares> = 6.x:

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

Estoy usando Angular 8 y la respuesta de @ franklin-pious resuelve el problema. En mi caso, obtener la URL anterior dentro de una suscripción causa algunos efectos secundarios si se adjunta con algunos datos en la vista.

La solución alternativa que utilicé fue enviar la URL anterior como parámetro opcional en la navegación de la ruta.

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

Y para obtener este valor en el componente:

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

this.router y this.route se inyectan dentro del constructor de cada componente y se importan como miembros @ angular / enrutador.

import { Router, ActivatedRoute }   from '@angular/router';
Víctor Oliveira
fuente
10

Angular 8 y rxjs 6 en la versión 2019

Me gustaría compartir la solución basada en otras grandes soluciones.

Primero cree un servicio para escuchar los cambios de rutas y guarde la última ruta anterior en un Asunto de comportamiento, luego proporcione este servicio en el componente principal de la aplicación en el constructor y luego use este servicio para obtener la ruta anterior que desee cuando lo desee.

caso de uso: desea redirigir al usuario a una página de publicidad y luego redirigirlo automáticamente al lugar de donde vino, por lo que necesita la última ruta anterior para hacerlo.

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

  }
}

proporcionar el servicio en app.module

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

Inyectarlo en app.component

  constructor(
    private routeEventsService: RouteEventsService
  )

finalmente use la ruta anterior guardada en el componente que desee

  onSkipHandler(){
    // navigate the user to where he did came from
    this.router.navigate([this.routeEventsService.previousRoutePath.value]);
  }
Louay Alosh
fuente
Esto funciona muy bien. Pero tengo una pregunta rápida. ¿Alguna vez se da de baja?
w0ns88
agregue tomar (1) así -> por pares (), tomar (1)). subscribe ((e: any)
Mukus
1
Tenga en cuenta que si utiliza @Injectable({ providedIn: 'root' })el servicio, se carga automáticamente en el módulo raíz (AppModule) del proyecto y, por lo tanto, no tiene que proporcionarlo manualmente al app.module. Consulte los documentos para obtener más detalles. No es necesario darse de baja del router Observables como se indica en esta respuesta
Hkidd
5

PARA ANGULAR 7+

En realidad, desde Angular 7.2 no es necesario utilizar un servicio para guardar la URL anterior. Puede usar el objeto de estado para establecer la última URL antes de vincular a la página de inicio de sesión. A continuación, se muestra un ejemplo de un escenario de inicio de sesión.

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

Además, también puede pasar los datos en la plantilla:

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

@ GünterZöchbauer también puedes guardarlo en localstorage pero yo no lo prefiero) mejor para ahorrar en servicio y obtener este valor de allí

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

Puede utilizar la ubicación como se menciona aquí .

Aquí está mi código si el enlace se abrió en una nueva pestaña

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

Importaciones requeridas

import { Router } from '@angular/router';
import { Location } from '@angular/common';
Waleed Emad
fuente
0

Bastante simple usando previousNavigationobjeto:

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

Tuve algunos problemas para acceder a la URL anterior dentro de un guardia.
Sin implementar una solución personalizada, esta me está funcionando.

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

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

La URL inicial será la página de URL anterior.

C0ZEN
fuente
-2

Todas las RESPUESTAS anteriores se cargarán URL varias veces. Si el usuario también visitó cualquier otro componente, este código se cargará.

Así que es mejor usar el concepto de creación de servicios. https://community.wia.io/d/22-access-the-previous-route-in-your-angular-5-app

Esto funcionará bien en todas las versiones de Angular. (¡Asegúrese de agregarlo a la matriz de proveedores en su archivo app.module!)

ABHILASHA KM
fuente
-2

Usando pares de rxjx puede lograr esto más fácilmente. importar {filtro, por pares} 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;
});

}

Ashraf Ali
fuente
-6

Tuve un problema similar cuando quise volver a la página anterior. La solución fue más fácil de lo que podía imaginar.

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

Y vuelve a la URL principal. Espero que ayude a alguien;)

DiPix
fuente
Esto no funcionará, le está diciendo que suba en la ruta del enrutador, en lugar de la URL anterior como indicó el OP.
Frederic Yesid Peña Sánchez
Esto no funciona si su URL es compleja con parámetros o si no tiene la misma ruta que la principal. Solo funciona si desea volver de "algo / padre / hijo" a "algo / padre".
A. El Idrissi