Как определить URL предыдущей страницы в Angular?

100

Предположим, я сейчас нахожусь на странице с URL-адресом /user/:id. Теперь с этой страницы я перехожу на следующую :id/posts.

Теперь Есть ли способ, так что я могу проверить , что предыдущий URL, то есть /user/:id.

Ниже мои маршруты

export const routes: Routes = [
  { 
    path: 'user/:id', component: UserProfileComponent
  },
  {  
    path: ':id/posts', component: UserPostsComponet 
  }
];
Чандра Шекхар
источник

Ответы:

80

Вы можете подписаться на изменения маршрута и сохранить текущее событие, чтобы использовать его, когда произойдет следующее.

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

См. Также Как определить изменение маршрута в Angular?

Гюнтер Цёхбауэр
источник
12
Спасибо, @ Günter. Ты всегда меня спасаешь.
Чандра Шекхар
29
Это не список для меня предыдущего маршрута, только текущий маршрут.
Дэвид Агирре
2
Зависит от того, чего вы ожидаете. В первый раз это nullпотому, что предыдущего маршрута нет. Вам также необходимо сделать это на корневом маршрутизаторе, иначе вы получите это только при переходе между дочерними маршрутами этого компонента.
Günter Zöchbauer
8
Это не дает предыдущий URL-адрес, когда конструктор выполняется в первый раз.
Ekaitz Hernandez Troyas
9
Какое значение вы ожидаете в качестве предыдущего URL-адреса при первом запуске конструктора?
Günter Zöchbauer
109

Возможно, все остальные ответы относятся к angular 2.X.

Теперь это не работает для angular 5.X. Я работаю с этим.

с помощью только NavigationEnd вы не можете получить предыдущий URL.

потому что Маршрутизатор работает от "NavigationStart", "RoutesRecognized", ... до "NavigationEnd".

Вы можете проверить с

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

Но вы все равно не можете получить предыдущий URL даже с "NavigationStart".

Теперь нужно использовать попарно.

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

С попарно вы можете увидеть, откуда и куда идет URL.

«RoutesRecognized» - это переход от исходного URL к целевому.

так что отфильтруйте его и получите от него предыдущий URL.

Последний, но тем не менее важный,

поместите этот код в родительский компонент или выше (например, app.component.ts)

потому что этот код запускается после завершения маршрутизации.

Обновление angular 6+

events.filterДает ошибку , потому что фильтр не является частью событий, поэтому изменить код на

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
источник
2
Реализован как сервис и отлично работает. Я использую угловой 6.1.7.
А. Эль Идрисси
5
@ tjvg1991 Обновление страницы означает, что вы потеряли данные в памяти. если вы сохраняете предыдущие данные, вам нужно использовать localStorage или cookie. (сохраняйте данные в своей локальной памяти, а не в памяти)
BYUNGJU JIN
Я просто хочу убить кнопку Upvote. Спасибо, чувак.
Мухаммад Умайр
@BYUNGJUJIN Спасибо за это!
Иоанн
@ BYUNGJU JIN, спасибо, работает для меня. как мне получить значение параметра из ссылки перенаправления, скажем, например, events [0] .urlAfterRedirects дает мне '/ inventoryDetails; test = 0; id = 45', я хочу получить значение id из этого. Как я могу обойтись без использования subString.
JNPW,
49

Создайте инъекционную услугу:

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

Тогда используйте его везде, где вам нужно. Чтобы сохранить текущую переменную как можно скорее, необходимо использовать службу в 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
Я считаю это наиболее элегантным решением. Попробуйте объединить этот код с новым фильтром и попарным решением: stackoverflow.com/a/35287471/518879
dangerous89
2
Пс. Не забудьте добавить этот RouterExtService в apps-routing.module.ts (в моем случае), например:@NgModule({ ..., providers: [RouterExtService]}) export class AppRoutingModule { }
dangerous89
Хорошо, есть большая проблема с этим сервисным решением. В моем случае я вызываю routerExtService.getPreviousUrl()метод в конструкторе сервиса, используемого в компоненте. По какой-то причине это вызвано раньше, чем само обновление. Это означает, что у нас есть временная зависимость! Я думаю, что использовать Subject намного проще.
dangerous89
Что ж, у меня это сработало в небольшом проекте. Может быть, нужно немного подправить его под ваши нужды. Вы решили проблему?
Джулиано
В настоящее время я использую так называемые параметры матрицы URL-адресов для «сохранения» моего состояния в моем URL-адресе, по умолчанию URL-адрес браузера сохраняет состояние сейчас при использовании кнопки возврата. 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 })
dangerous89
20

Обновленный код Angular 6 для получения предыдущего URL-адреса в виде строки.

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
            });
    }
Франклин Благочестивый
источник
Это возвращает URL-адрес, который был заблокирован охранником, есть ли способ получить только предыдущий URL-адрес, который был активирован (не заблокирован охранником)?
Exocomp
1
Любой намек на лучший способ отписаться от наблюдаемого маршрутизатора?
j4v1
Работает! Я действительно не знаю, почему "NavigationEnd" не работает
davidwillianx
13

Это сработало для меня в версиях angular> = 6.x:

this.router.events
            .subscribe((event) => {
              if (event instanceof NavigationStart) {
                window.localStorage.setItem('previousUrl', this.router.url);
              }
            });
Правин Панди
источник
10

Я использую Angular 8, и ответ @ franklin-pious решает проблему. В моем случае получение предыдущего URL-адреса внутри подписки вызывает некоторые побочные эффекты, если он прикреплен с некоторыми данными в представлении.

Обходной путь, который я использовал, заключался в отправке предыдущего URL-адреса в качестве необязательного параметра в навигации по маршруту.

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

И чтобы получить это значение в компоненте:

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

this.router и this.route вводятся внутри конструктора каждого компонента и импортируются как члены @ angular / router.

import { Router, ActivatedRoute }   from '@angular/router';
Виктор Оливейра
источник
10

Angular 8 и rxjs 6 в версии 2019 года

Я хотел бы поделиться решением, основанным на других отличных решениях.

Сначала создайте службу для прослушивания изменений маршрутов и сохранения последнего предыдущего маршрута в субъекте поведения, затем предоставьте эту службу в основном компоненте app.component в конструкторе, а затем используйте эту службу, чтобы получить предыдущий маршрут, который вы хотите, в любое время.

вариант использования: вы хотите перенаправить пользователя на страницу с рекламой, а затем автоматически перенаправить его / ее туда, откуда он пришел, поэтому для этого вам понадобится последний предыдущий маршрут.

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

  }
}

предоставить услугу в app.module

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

Ввести его в app.component

  constructor(
    private routeEventsService: RouteEventsService
  )

наконец, используйте сохраненный предыдущий маршрут в желаемом компоненте

  onSkipHandler(){
    // navigate the user to where he did came from
    this.router.navigate([this.routeEventsService.previousRoutePath.value]);
  }
Луай Алош
источник
Это действительно хорошо работает. Но у меня небольшой вопрос. Вы когда-нибудь отписывались?
w0ns88
добавить take (1) вот так -> pairwise (), take (1)). subscribe ((e: any)
Мукус
1
Обратите внимание: если вы используете @Injectable({ providedIn: 'root' })сервис, он автоматически загружается в корневой модуль (AppModule) проекта, и поэтому вам не нужно вручную предоставлять его в app.module. Подробности см. В документации . Нет необходимости отказываться от подписки на маршрутизатор Observables, как указано в этом ответе
Hkidd,
5

ДЛЯ УГЛОВОГО 7+

На самом деле, начиная с Angular 7.2, нет необходимости использовать службу для сохранения предыдущего URL-адреса. Вы можете просто использовать объект состояния, чтобы установить последний URL-адрес перед ссылкой на страницу входа. Вот пример сценария входа в систему.

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

Кроме того, вы также можете передать данные в шаблоне:

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

@ GünterZöchbauer, также вы можете сохранить его в localstorage, но я не предпочитаю его) лучше сохранить в сервисе и получить это значение оттуда

 constructor(
        private router: Router
      ) {
        this.router.events
          .subscribe((event) => {
            if (event instanceof NavigationEnd) {
              localStorage.setItem('previousUrl', event.url);
            }
          });
      }
владими
источник
3

Вы можете использовать Location, как указано здесь .

Вот мой код, если ссылка открылась на новой вкладке

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

Обязательный импорт

import { Router } from '@angular/router';
import { Location } from '@angular/common';
Валид Эмад
источник
0

Довольно просто с помощью previousNavigationобъекта:

this.router.events
  .pipe(
    filter(e => e instanceof NavigationEnd && this.router.getCurrentNavigation().previousNavigation),
    map(() => this.router.getCurrentNavigation().previousNavigation.finalUrl.toString()),
  )
  .subscribe(previousUrl => {}); 
Дмитрий Куба
источник
0

Мне было трудно получить доступ к предыдущему URL-адресу внутри охранника.
Без реализации специального решения это работает для меня.

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

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

Первым URL будет предыдущая страница URL.

C0ZEN
источник
-2

Все вышеперечисленные ответы будут загружать URL несколько раз. Если пользователь также посетил любой другой компонент, этот код загрузится.

Так что лучше использовать концепцию создания службы. https://community.wia.io/d/22-access-the-previous-route-in-your-angular-5-app

Это будет хорошо работать во всех версиях Angular. (Обязательно добавьте его в массив поставщиков в файле app.module!)

АБХИЛАША КМ
источник
-2

Используя попарно из rxjx, вы можете добиться этого проще. импортировать {фильтр, попарно} из 'rxjs / operator';

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

У меня была аналогичная проблема, когда я хотел вернуться на предыдущую страницу. Решение было проще, чем я мог представить.

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

И он возвращается на родительский URL. Надеюсь, это кому-то поможет;)

DiPix
источник
Это не сработает, вы говорите подняться по пути маршрутизатора, а не по предыдущему URL-адресу, как указано в OP.
Фредерик Йесид Пенья Санчес
Это не работает, если ваш URL-адрес сложен с параметрами или не имеет того же пути, что и родительский. Это работает, только если вы хотите вернуться от «что-то / родитель / ребенок» к «чему-то / родительскому элементу».
А. Эль Идрисси