Jak określić adres URL poprzedniej strony w Angular?

104

Załóżmy, że aktualnie jestem na stronie, która ma adres URL /user/:id. Teraz z tej strony przechodzę do następnej strony :id/posts.

Czy jest sposób, żebym mógł sprawdzić, jaki jest poprzedni adres URL, tj /user/:id.

Poniżej moje trasy

export const routes: Routes = [
  { 
    path: 'user/:id', component: UserProfileComponent
  },
  {  
    path: ':id/posts', component: UserPostsComponet 
  }
];
Chandra Shekhar
źródło

Odpowiedzi:

83

Możesz zasubskrybować zmiany tras i przechowywać bieżące wydarzenie, aby móc z niego skorzystać, gdy wydarzy się następne

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

Zobacz także Jak wykryć zmianę trasy w Angular?

Günter Zöchbauer
źródło
12
Dzięki @ Günter Zawsze ratujesz mój dzień.
Chandra Shekhar
34
Nie jest to dla mnie lista poprzedniej trasy, tylko aktualna trasa.
David Aguirre,
2
Zależy od tego, czego oczekujesz. Za pierwszym razem to nulldlatego , że nie ma poprzedniej trasy. Musisz to również zrobić na routerze głównym, w przeciwnym razie uzyskasz tylko podczas nawigacji między trasami podrzędnymi tego komponentu.
Günter Zöchbauer
8
To nie daje poprzedniego adresu URL, gdy konstruktor jest wykonywany po raz pierwszy.
Ekaitz Hernandez Troyas
9
Jakiej wartości oczekujesz jako poprzedniego adresu URL, gdy konstruktor jest wykonywany po raz pierwszy?
Günter Zöchbauer
114

Może wszystkie inne odpowiedzi dotyczą Angular 2.X.

Teraz nie działa dla kątowej 5.X. Pracuję z tym.

mając tylko NavigationEnd, nie można uzyskać poprzedniego adresu URL.

ponieważ Router działa od „NavigationStart”, „RoutesRecognized”, ..., do „NavigationEnd”.

Możesz to sprawdzić

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

Ale nadal nie możesz uzyskać poprzedniego adresu URL, nawet z "NavigationStart".

Teraz musisz użyć parowania.

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

Dzięki parowaniu możesz zobaczyć, jaki jest adres URL od i do.

„RoutesRecognized” to krok zmiany od początku do docelowego adresu URL.

więc przefiltruj go i pobierz z niego poprzedni adres URL.

Nie mniej ważny,

umieść ten kod w komponencie nadrzędnym lub wyższym (np. app.component.ts)

ponieważ ten kod jest uruchamiany po zakończeniu routingu.

Zaktualizuj kątową 6+

events.filterDaje błąd, ponieważ filtr nie jest częścią wydarzeń, więc zmiany kodu do

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
źródło
2
Wdrożony jako usługa i działa świetnie. Używam kątowego 6.1.7.
A. El Idrissi
5
@ tjvg1991 Odświeżająca się strona oznacza utratę danych pamięci. jeśli zachowujesz poprzednie dane, musisz użyć localStorage lub cookie. (zapisuj dane w lokalnej pamięci, a nie w pamięci)
BYUNGJU JIN
@BYUNGJUJIN Dzięki za to!
john,
1
@BYUNGJUIN przechowuje zwracaną wartość subscribe()w polu i wywołuje unsubscribe()ją w ngOnDestroy(). Powinny być odcinki. pytania z przykładami anulowania subskrypcji tutaj na SO.
Günter Zöchbauer
1
@app, sprawdź to, aby zrezygnować z subskrypcji: malcontentboffin.com/2017/12/…
BYUNGJU JIN
51

Utwórz usługę do wstrzykiwania:

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

Następnie używaj go wszędzie tam, gdzie potrzebujesz. Aby jak najszybciej zapisać aktualną zmienną, konieczne jest skorzystanie z usługi w 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
źródło
2
Myślę, że to najbardziej eleganckie rozwiązanie. Spróbuj połączyć ten kod z nowym filtrem i rozwiązaniem parami: stackoverflow.com/a/35287471/518879
niebezpieczeństwo89
2
Ps. Nie zapomnij dodać tego RouterExtService do pliku apps-routing.module.ts (w moim przypadku), na przykład:@NgModule({ ..., providers: [RouterExtService]}) export class AppRoutingModule { }
niebezpieczeństwo89
OK, jest duży problem z tym rozwiązaniem usługi. W moim przypadku routerExtService.getPreviousUrl()metodę wywołuję w konstruktorze usługi używanej w komponencie. Z jakiegoś powodu zadzwoniło to wcześniej niż faktyczna aktualizacja. Oznacza to, że mamy zależność czasową! Myślę, że temat jest znacznie łatwiejszy w użyciu.
niebezpieczeństwo89
Cóż, działało dobrze dla mnie w małym projekcie. Może wymaga pewnych poprawek, aby dopasować je do Twoich potrzeb. Czy rozwiązałeś problem?
Juliano
Obecnie używam tak zwanych parametrów macierzy adresów URL, aby „przechowywać” mój stan w moim adresie URL, domyślnie adres URL przeglądarki przechowuje stan teraz, gdy używam przycisku Wstecz. 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 })
niebezpieczeństwo89
20

Angular 6 zaktualizował kod do pobierania poprzedniego adresu URL jako łańcucha.

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 Pious
źródło
Powoduje to zwrócenie adresu URL, który został zablokowany przez strażnika. Czy istnieje sposób, aby uzyskać tylko poprzedni adres URL, który został aktywowany (nie został zablokowany przez strażnika)?
Exocomp
1
Czy są jakieś wskazówki dotyczące najlepszego sposobu wypisania się z routera?
j4v1
Pracuje! Naprawdę nie wiem, dlaczego „NavigationEnd” nie działa
davidwillianx,
13

To zadziałało dla mnie w wersjach kątowych> = 6.x:

this.router.events
            .subscribe((event) => {
              if (event instanceof NavigationStart) {
                window.localStorage.setItem('previousUrl', this.router.url);
              }
            });
Praveen Pandey
źródło
11

Używam Angular 8 i odpowiedź @ franklin-pious rozwiązuje problem. W moim przypadku pobranie poprzedniego adresu URL w ramach subskrypcji powoduje pewne skutki uboczne, jeśli jest dołączone do niektórych danych w widoku.

Obejście, którego użyłem, polegało na wysłaniu poprzedniego adresu URL jako opcjonalnego parametru w nawigacji po trasie.

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

Aby uzyskać tę wartość w komponencie:

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

this.router i this.route są wstawiane do konstruktora każdego komponentu i importowane jako elementy @ angular / router.

import { Router, ActivatedRoute }   from '@angular/router';
Victor Oliveira
źródło
10

Angular 8 i rxjs 6 w wersji 2019

Chciałbym podzielić się rozwiązaniem opartym na innych świetnych rozwiązaniach.

Najpierw wykonaj usługę nasłuchiwania zmian tras i zapisz ostatnią poprzednią trasę w temacie zachowania, a następnie udostępnij tę usługę w głównym komponencie app.com w konstruktorze, a następnie użyj tej usługi, aby uzyskać poprzednią trasę, którą chcesz, kiedy chcesz.

przypadek użycia: chcesz przekierować użytkownika na stronę z ogłoszeniami, a następnie automatycznie przekierować go / ją tam, skąd przyszedł, więc potrzebujesz ostatniej poprzedniej trasy, aby to zrobić.

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

  }
}

świadczyć usługę w app.module

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

Wstrzyknij go w app.component

  constructor(
    private routeEventsService: RouteEventsService
  )

na koniec użyj zapisanej poprzedniej trasy w wybranym komponencie

  onSkipHandler(){
    // navigate the user to where he did came from
    this.router.navigate([this.routeEventsService.previousRoutePath.value]);
  }
Louay Alosh
źródło
To działa naprawdę dobrze. Ale mam krótkie pytanie. Czy kiedykolwiek wypisałeś się z subskrypcji?
w0ns88
dodaj take (1) w ten sposób -> pairwise (), take (1)). subskrybuj ((e: any)
Mukus
1
Zwróć uwagę, że jeśli korzystasz @Injectable({ providedIn: 'root' })z usługi, jest ona automatycznie ładowana do modułu głównego (AppModule) projektu, a zatem nie musisz ręcznie dostarczać jej do app.module. Szczegółowe informacje można znaleźć w dokumentacji . Nie jest konieczne anulowanie subskrypcji routera Observables, jak podano w tej odpowiedzi
Hkidd
7

DLA KĄTOWEGO 7+

Właściwie od Angulara 7.2 nie ma potrzeby korzystania z usługi do zapisywania poprzedniego adresu URL. Możesz po prostu użyć obiektu stanu, aby ustawić ostatni adres URL przed połączeniem ze stroną logowania. Oto przykład scenariusza logowania.

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

Dodatkowo możesz też przekazać dane w szablonie:

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

@ GünterZöchbauer również możesz zapisać go w lokalnym magazynie, ale nie wolę tego) lepiej zapisać w serwisie i stamtąd uzyskać tę wartość

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

Możesz użyć lokalizacji, jak wspomniano tutaj .

Oto mój kod, jeśli link został otwarty w nowej karcie

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

Wymagany import

import { Router } from '@angular/router';
import { Location } from '@angular/common';
Waleed Emad
źródło
0

Całkiem proste przy użyciu previousNavigationobiektu:

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

Miałem trudności z uzyskaniem dostępu do poprzedniego adresu URL wewnątrz strażnika.
Bez wdrażania niestandardowego rozwiązania to działa dla mnie.

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

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

Początkowy adres URL będzie poprzednią stroną adresu URL.

C0ZEN
źródło
0

To proste rozwiązanie zadziałało dla mnie.

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
        });
    };
}
Sahil Ralkar
źródło
-2

Wszystkie powyższe ODPOWIEDZI będą ładować URL wiele razy. Jeśli użytkownik odwiedził również inny komponent, ten kod zostanie załadowany.

Więc lepiej w użyciu, koncepcja tworzenia usług. https://community.wia.io/d/22-access-the-previous-route-in-your-angular-5-app

Działa to dobrze we wszystkich wersjach Angular. (Upewnij się, że dodajesz go do tablicy dostawców w pliku app.module!)

ABHILASHA KM
źródło
-2

Używając parami z rxjx, możesz to osiągnąć łatwiej. import {filter, pairwise} z 'rxjs / operators';

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
źródło
-6

Miałem podobny problem, gdy chciałem wrócić do poprzedniej strony. Rozwiązanie było łatwiejsze, niż mogłem sobie wyobrazić.

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

I wraca do nadrzędnego adresu URL. Mam nadzieję, że komuś to pomoże;)

DiPix
źródło
To nie zadziała, mówisz, aby przejść do ścieżki routera, a nie do poprzedniego adresu URL, jak podał OP.
Frederic Yesid Peña Sánchez
To nie działa, jeśli Twój adres URL jest złożony z parametrami lub ma inną ścieżkę niż nadrzędny. Działa tylko wtedy, gdy chcesz wrócić z „czegoś / rodzica / dziecka” do „czegoś / rodzica”.
A. El Idrissi