Как обнаружить изменение маршрута в 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.