Как обнаружить изменение маршрута в Angular?


429

Я ищу, чтобы обнаружить изменение маршрута в моем AppComponent.

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

Ответы:


535

В Angular 2 вы можете subscribe(событие Rx) к экземпляру Router. Так что вы можете делать такие вещи, как

class MyClass {
  constructor(private router: Router) {
    router.subscribe((val) => /*whatever*/)
  }
}

Изменить (начиная с rc.1)

class MyClass {
  constructor(private router: Router) {
    router.changes.subscribe((val) => /*whatever*/)
  }
}

Изменить 2 (с 2.0.0)

смотри также: Router.events doc

class MyClass {
  constructor(private router: Router) {
    router.events.subscribe((val) => {
        // see also 
        console.log(val instanceof NavigationEnd) 
    });
  }
}

2
Я могу получить данные, event._root.children[0].value._routeConfig.dataя надеюсь, что может быть лучше
Акшай

6
@Akshay Вы видели эту статью Тодда Мотто: [Динамические заголовки страниц в Angular 2 с событиями маршрутизатора] ( toddmotto.com/dynamic-page-titles-angular-2-router-events )
Богак

10
почему он срабатывает 3 раза?
Инструментарий

2
@Toolkit его, потому что событие имеет 3 состояния и при успешном изменении URL. 3 состояния: «NavigationStart», «NavigationEnd» и «RoutesRecognized»
RicardoGonzales

10
Вы можете легко фильтровать события с filterоператором RxJS . router.events.pipe(filter(e => e instanceof NavigationEnd).subscribe((e) => { ... }
Simon_Weaver

314

RxJS 6

router.events.pipe(filter(event => event instanceof NavigationStart))

Благодаря Peilonrayz (см. Комментарии ниже)

новый роутер> = RC.3

import { Router, NavigationStart, NavigationEnd, NavigationError, NavigationCancel, RoutesRecognized } from '@angular/router';

constructor(router:Router) {
  router.events.forEach((event) => {
    if(event instanceof NavigationStart) {
    }
    // NavigationEnd
    // NavigationCancel
    // NavigationError
    // RoutesRecognized
  });
}

Вы также можете фильтровать по данному событию:

import 'rxjs/add/operator/filter';

constructor(router:Router) {
  router.events
    .filter(event => event instanceof NavigationStart)
    .subscribe((event:NavigationStart) => {
      // You only receive NavigationStart events
    });
}

Использование pairwiseоператора для получения предыдущего и текущего события также является хорошей идеей. https://github.com/angular/angular/issues/11268#issuecomment-244601977

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

export class AppComponent {
    constructor(private router: Router) {
        this.router.events.pairwise().subscribe((event) => {
            console.log(event);
        });
    };
}

1
@GunterZochbauer вместо 'is' я бы использовал 'instanceof'. И «событие: событие» должно быть в скобках. Спасибо за это, довольно мощная новая функция! Мне это нравится
Максим

2
Это приводит к ошибке компиляции в текущих версияхArgument of type '(event: Event) => void' is not assignable to parameter of type
Rudi Strydom

1
@RudiStrydom & Günter Zöchbauer - Argument of type '(event: Event) => void' is not assignable to parameter of typeошибка в том, что в своем фрагменте фильтра вы подписываетесь на объект типа Event вместо NavigationEvent.
Бонни

1
Второй пример должен быть NavigationEvent вместо Event. Также не забудьте импортировать «Event as NavigationEvent» из @ angular / router
Мик

1
Намек на импорт был для тех, кто хочет решить эту ошибку :)
Мик

92

Для Angular 7 кто-то должен написать так:

this.router.events.subscribe((event: Event) => {})


Подробный пример может быть следующим:

import { Component } from '@angular/core'; 
import { Router, Event, NavigationStart, NavigationEnd, NavigationError } from '@angular/router';

@Component({
    selector: 'app-root',
    template: `<router-outlet></router-outlet>`
})
export class AppComponent {

    constructor(private router: Router) {

        this.router.events.subscribe((event: Event) => {
            if (event instanceof NavigationStart) {
                // Show loading indicator
            }

            if (event instanceof NavigationEnd) {
                // Hide loading indicator
            }

            if (event instanceof NavigationError) {
                // Hide loading indicator

                // Present error to user
                console.log(event.error);
            }
        });

   }
}

2
Это замечательно! очень всеобъемлющий! отлично работал на Angular 7.
MaylorTaylor

1
Обычно сама навигация занимает очень мало времени, если вы используете стратегию предварительной загрузки. С точки зрения удобства использования я бы использовал индикаторы загрузки только для http-запросов бэкэнда, если вообще использовал.
Фил

5
В конструкторе вы не должны использовать <this>, ваш случай для ngOnInit.
Серхио Рейс

1
Отлично, Как я могу получить точный param.id URL?
ShibinRagh

2
Решение не ограничено компонентами, оно распространяется по всему приложению, потребляя ресурсы
Md. Rafee

54

Угловой 7 , если вы хотите , subscribeчтобыrouter

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

import { filter } from 'rxjs/operators';

constructor(
  private router: Router
) {
  router.events.pipe(
    filter(event => event instanceof NavigationEnd)  
  ).subscribe((event: NavigationEnd) => {
    console.log(event.url);
  });
}

2
это не захватывает событие перенаправления
Anandhu Ajayakumar

40

Угловой 4.x и выше:

Это может быть достигнуто с помощью свойства url класса ActivatedRoute, как показано ниже,

this.activatedRoute.url.subscribe(url =>{
     console.log(url);
});

Примечание: что вам нужно импортировать и внедрить провайдера из angular/routerпакета

import { ActivatedRoute } from '@angular/router`

а также

constructor(private activatedRoute : ActivatedRoute){  }

18

Маршрутизатор 3.0.0-бета.2 должен быть

this.router.events.subscribe(path => {
  console.log('path = ', path);
});

это работает для текущего пути, но как насчет предыдущего?
Тацу

16

В угловых 6 и RxJS6:

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

 this.router.events.pipe(
      filter((event) => event instanceof NavigationEnd),
      debounceTime(40000)
    ).subscribe(
      x => {
      console.log('val',x);
      this.router.navigate(['/']); /*Redirect to Home*/
}
)

3
вы пропустили импорт для роутераimport {Router, NavigationEnd} from "@angular/router"
Дамир Бейлханов

15

Ответы здесь верны router-deprecated. Для последней версии router:

this.router.changes.forEach(() => {
    // Do whatever in here
});

или

this.router.changes.subscribe(() => {
     // Do whatever in here
});

Чтобы увидеть разницу между ними, пожалуйста, проверьте этот вопрос .

редактировать

Для последней вы должны сделать:

this.router.events.subscribe(event: Event => {
    // Handle route change
});

Предоставляет ли он какие-либо данные предыдущего и текущего маршрута?
Ак

Это routerбыло обновлено снова (я еще не обновил свой ответ), поэтому я не уверен, как это для последних. За то, о чем routerя писал, ты не мог. @akn
Дели

Пожалуйста, не могли бы вы предоставить контекст для этого ответа? какие линии вы заменяете в других решениях своими?
Джерард Симпсон

12

В Angular 8 вы должны делать как this.router.events.subscribe((event: Event) => {})

Пример:

import { Component } from '@angular/core'; 
import { Router, Event } from '@angular/router';
import { NavigationStart, NavigationError, NavigationEnd } from '@angular/router';

@Component({
    selector: 'app-root',
    template: `<router-outlet></router-outlet>`
})
export class AppComponent {

    constructor(private router: Router) {
        //Router subscriber
        this.router.events.subscribe((event: Event) => {
            if (event instanceof NavigationStart) {
                //do something on start activity
            }

            if (event instanceof NavigationError) {
                // Handle error
                console.error(event.error);
            }

            if (event instanceof NavigationEnd) {
                //do something on end activity
            }
        });
   }
}

10

В компоненте вы можете попробовать это:

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

constructor(private router: Router) {
router.events.subscribe(
        (event) => {
            if (event instanceof NavigationStart)
                // start loading pages
            if (event instanceof NavigationEnd) {
                // end of loading paegs
            }
        });
}

8

Захват событий изменения маршрута следующим образом ...

import { Component, OnInit, Output, ViewChild } from "@angular/core";
import { Router, NavigationStart, NavigationEnd, Event as NavigationEvent } from '@angular/router';

@Component({
    selector: "my-app",
    templateUrl: "app/app.component.html",
    styleUrls: ["app/app.component.css"]
})
export class AppComponent {

    constructor(private cacheComponentObj: CacheComponent,
        private router: Router) {

        /*  Route event types
            NavigationEnd
            NavigationCancel
            NavigationError
            RoutesRecognized
        */
        router.events.forEach((event: NavigationEvent) => {

            //Before Navigation
            if (event instanceof NavigationStart) {
                switch (event.url) {
                case "/app/home":
                {
                    //Do Work
                    break;
                }
                case "/app/About":
                {
                    //Do Work
                    break;
                }
                }
            }

            //After Navigation
            if (event instanceof NavigationEnd) {
                switch (event.url) {
                case "/app/home":
                {
                    //Do Work
                    break;
                }
                case "/app/About":
                {
                    //Do Work
                    break;
                }
                }
            }
        });
    }
}

Отлично, Как я могу получить точный param.id URL?
ShibinRagh

6

Расположение работает ...

import {Component, OnInit} from '@angular/core';
import {Location} from '@angular/common';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.scss']
})

export class AppComponent implements OnInit {

    constructor(private location: Location) {
        this.location.onUrlChange(x => this.urlChange(x));
    }

    ngOnInit(): void {}

    urlChange(x) {
        console.log(x);
    }
}

Хороший ответ. это работает для моего случая. Спасибо
Умар Тарик

4

Выше большинство решений верны, но я сталкиваюсь с проблемой, которая вызывает многократное событие 'Navigation emit'. Когда я менял любой маршрут, это событие вызывается. Так что слушайте это полное решение для Angular 6.

import { Subscription } from 'rxjs/Subscription';
import 'rxjs/add/operator/do';
import 'rxjs/add/operator/filter';    

export class FooComponent implements OnInit, OnDestroy {
   private _routerSub = Subscription.EMPTY;
   constructor(private router: Router){}

   ngOnInit(){
     this._routerSub = this.router.events
      .filter(event => event instanceof NavigationEnd)
      .subscribe((value) => {
         //do something with the value
     });
  }

  ngOnDestroy(){
   this._routerSub.unsubscribe();
  }
} 

3

@Ludohen ответ отличный, но если вы не хотите использовать, instanceofиспользуйте следующее

this.router.events.subscribe(event => {
  if(event.constructor.name === "NavigationStart") {
    // do something...
  }
});

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


3
почему бы не использовать безопасность машинописи?
Паскаль

@ Паскаль, почему ненавидишь? и этот Eventтип вызывает ошибку в Atom, поэтому я не использовал его
Халед Аль-Ансари

2
@Pascal нет, это проблема Angular, поскольку событие маршрутизатора не совпадает с событием браузера, и поэтому тип события не будет работать! им нужно создать новый интерфейс для этого события, я должен был сказать это с самого начала, но необоснованное
Халед Аль-Ансари

5
поскольку минимизация выполняется в рабочем коде, вы должны использовать его, instanceOfчтобы ваш пример работал и в рабочем коде. if(event instanceOf NavigationStart) {
Милан Ярич

1
Должно бытьif(event instanceof NavigationStart)
кетан

1

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

Представляет событие, вызванное успешным завершением навигации.

Как это использовать?

import { Component, OnInit } from '@angular/core';
import { Router, ActivatedRouteSnapshot, NavigationEnd } from '@angular/router';
@Component({
    selector: 'app-navbar',
    templateUrl: './navbar.component.html',
    styleUrls: ['./navbar.component.css']
})
export class NavbarComponent implements OnInit {
    constructor(private router: Router) { }
    ngOnInit(): void {
        //calls this method when navigation ends
        this.router.events.subscribe(event => {
            if (event instanceof NavigationEnd) {
                //calls this stuff when navigation ends
                console.log("Event generated");
            }
        });
    }
}

Когда использовать это?

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

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


0

Следующий ВИД работ и может сделать хитрый для вас.

// in constructor of your app.ts with router and auth services injected
router.subscribe(path => {
    if (!authService.isAuthorised(path)) //whatever your auth service needs
        router.navigate(['/Login']);
    });

К сожалению, это перенаправляет позже в процессе маршрутизации, чем я хотел бы. onActivate()Первоначального целевого компонента вызывается перед перенаправлением.

На @CanActivateцелевом компоненте можно использовать декоратор, но это а) не централизовано и б) не извлекает выгоду из внедренных сервисов.

Было бы здорово, если бы кто-нибудь мог предложить лучший способ централизованной авторизации маршрута до его фиксации. Я уверен, что должен быть лучший способ.

Это мой текущий код (как бы я изменил его, чтобы прослушать изменение маршрута?):

import {Component, View, bootstrap, bind, provide} from 'angular2/angular2';
import {ROUTER_BINDINGS, RouterOutlet, RouteConfig, RouterLink, ROUTER_PROVIDERS, APP_BASE_HREF} from 'angular2/router';    
import {Location, LocationStrategy, HashLocationStrategy} from 'angular2/router';

import { Todo } from './components/todo/todo';
import { About } from './components/about/about';

@Component({
    selector: 'app'
})

@View({
    template: `
        <div class="container">
            <nav>
                <ul>
                    <li><a [router-link]="['/Home']">Todo</a></li>
                    <li><a [router-link]="['/About']">About</a></li>
                </ul>
            </nav>
            <router-outlet></router-outlet>
        </div>
    `,
    directives: [RouterOutlet, RouterLink]
})

@RouteConfig([
    { path: '/', redirectTo: '/home' },
    { path: '/home', component: Todo, as: 'Home' },
    { path: '/about', component: About, as: 'About' }
])

class AppComponent {    
    constructor(location: Location){
        location.go('/');
    }    
}    
bootstrap(AppComponent, [ROUTER_PROVIDERS, provide(APP_BASE_HREF, {useValue: '/'})]);

Я видел, как люди расширяют routerOutlet, чтобы добавить свой код авторизации, который является одним из способов. На gitHub есть разговоры об этом, но пока нет никаких выводов. Вот способ Auth0
Деннис Смолек

Благодарю за ваш ответ. Знаете ли вы хорошие видео для изучения authService для angular 2?
AngularM

0

Я делаю это так с RC 5

this.router.events
  .map( event => event instanceof NavigationStart )
  .subscribe( () => {
    // TODO
  } );

0

Просто внесите изменения в AppRoutingModule, как

@NgModule({
imports: [RouterModule.forRoot(routes, { scrollPositionRestoration: 'enabled' })],
  exports: [RouterModule]
})

0

Angular 8. Проверьте, является ли текущий маршрут базовым .

  baseroute: boolean;
  constructor(
    private router: Router,
  ) {
    router.events.subscribe((val: any) => {
      if (val.url == "/") {
        this.baseroute = true;
      } else {
        this.baseroute = false;
      }
    });
  }

0

Я бы написал что-то вроде этого:

ngOnInit() {
this.routed = this.router.events.map( event => event instanceof NavigationStart )
  .subscribe(() => {
  } );
}

ngOnDestroy() {
this.routed.unsubscribe();
}

-3

простой ответ для Angular 8. *

constructor(private route:ActivatedRoute) {
  console.log(route);
}

1
Разве это не выполняется только при создании экземпляра, это будет: только один раз !? Это не решение.
Сатрия
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.