Jak wykryć zmianę trasy w Angular?

428

Chcę wykryć zmianę trasy w mojej AppComponent .

Następnie sprawdzę globalny token użytkownika, aby sprawdzić, czy jest on zalogowany. Następnie mogę przekierować użytkownika, jeśli nie jest zalogowany.

AngularM
źródło

Odpowiedzi:

534

W Angular 2 możesz subscribe(zdarzenie Rx) do instancji routera. Możesz więc robić takie rzeczy jak

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

Edytuj (od rc.1)

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

Edycja 2 (od 2.0.0)

patrz także: Router.events doc

class MyClass {
  constructor(private router: Router) {
    router.events.subscribe((val) => {
        // see also 
        console.log(val instanceof NavigationEnd) 
    });
  }
}
Ludohen
źródło
2
Jestem w stanie uzyskać dane, event._root.children[0].value._routeConfig.datamam nadzieję, że może być lepszy sposób
Akshay,
6
@Ashshay widziałeś ten artykuł Todda Motto: [Dynamiczne tytuły stron w Angular 2 ze zdarzeniami routera] ( toddmotto.com/dynamic-page-titles-angular-2-router-events )
Bogac
10
dlaczego strzela 3 razy?
Zestaw narzędzi
2
@Toolkit jego, ponieważ wydarzenie ma 3 stany i po pomyślnej zmianie adresu URL. Te 3 stany to: „NavigationStart”, „NavigationEnd” i „RoutesRecognized”
RicardoGonzales,
10
możesz łatwo filtrować zdarzenia za pomocą filteroperatora RxJS . router.events.pipe(filter(e => e instanceof NavigationEnd).subscribe((e) => { ... }
Simon_Weaver
314

RxJS 6

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

Dzięki Peilonrayz (patrz komentarze poniżej)

nowy router> = 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
  });
}

Możesz również filtrować według danego zdarzenia:

import 'rxjs/add/operator/filter';

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

Dobrym pomysłem jest także skorzystanie z pairwiseoperatora w celu uzyskania poprzedniego i bieżącego wydarzenia. 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);
        });
    };
}
Günter Zöchbauer
źródło
1
@GunterZochbauer zamiast „is” użyłbym „instanceof”. „Zdarzenie: Zdarzenie” powinno być w nawiasach. Dzięki za to, całkiem potężna nowa funkcja! Podoba mi się
Maxim
2
To powoduje błąd kompilacji w bieżących wersjachArgument 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 typebłąd polega na tym, że we fragmencie filtra subskrybujesz obiekt typu Event zamiast NavigationEvent.
Bonnici
1
Drugą próbką powinno być NavigationEvent zamiast Event. Nie zapomnij również zaimportować „Event as NavigationEvent” z @ angular / router
Mick
1
Wskazówka dotycząca importu była dla każdego, kto chce rozwiązać ten błąd :)
Mick
91

W Angular 7 ktoś powinien napisać:

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


Szczegółowy przykład może wyglądać następująco:

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

   }
}
Сергій Козюк
źródło
2
To jest świetne! bardzo wszechstronne! działał doskonale na Angular 7.
MaylorTaylor
1
Zwykle sama nawigacja zajmuje bardzo mało czasu, jeśli używasz strategii wstępnego ładowania. Jeśli chodzi o użyteczność, używałbym wskaźników ładowania tylko na żądanie HTTP zaplecza, jeśli w ogóle.
Phil
5
W konstruktorze nie powinieneś używać <to>, twoja sprawa dotyczy ngOnInit.
Sergio Reis,
1
Idealnie, jak mogę uzyskać dokładny param.id adresu URL?
ShibinRagh
2
Rozwiązanie nie jest ograniczone do komponentu, rozprzestrzenia się w aplikacji, zużywa zasoby
Md. Rafee
54

Kątowe 7 , jeśli chcesz subscribe, abyrouter

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);
  });
}
Druta Ruslan
źródło
2
nie przechwytuje zdarzenia przekierowania
Anandhu Ajayakumar
40

Kątowy 4.xi wyższy:

Można to osiągnąć za pomocą właściwości url klasy ActivatedRoute, jak poniżej,

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

Uwaga: Musisz zaimportować i wstrzyknąć dostawcę z angular/routerpaczki

import { ActivatedRoute } from '@angular/router`

i

constructor(private activatedRoute : ActivatedRoute){  }
Aravind
źródło
18

Router 3.0.0-beta.2 powinien być

this.router.events.subscribe(path => {
  console.log('path = ', path);
});
Mike Lallemont
źródło
to działa na bieżącej ścieżce, ale co z poprzednimi?
tatsu
16

W kątach 6 i 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*/
}
)
JBRandri
źródło
3
przegapiłeś import dla routeraimport {Router, NavigationEnd} from "@angular/router"
Damir Beylkhanov
15

Odpowiedzi tutaj są poprawne dla router-deprecated. Najnowsza wersja router:

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

lub

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

Aby zobaczyć różnicę między nimi, sprawdź to SO pytanie .

Edytować

Najpóźniej musisz zrobić:

this.router.events.subscribe(event: Event => {
    // Handle route change
});
Dehli
źródło
Czy zawiera dane dotyczące poprzedniej i bieżącej trasy?
akn
routerZostał ponownie zaktualizowany (I nie zostały zaktualizowane jeszcze moją odpowiedź), więc nie jestem pewien, jak to jest ostatni. W przypadku router, o którym pisałem, nie można. @akn
Dehli
Czy mógłbyś podać kontekst dla tej odpowiedzi? które linie zamieniasz w innych rozwiązaniach na swoje?
Gerard Simpson,
12

W Angular 8 powinieneś to lubić this.router.events.subscribe((event: Event) => {})

Przykład:

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
            }
        });
   }
}
Rohit.007
źródło
10

W komponencie możesz spróbować tego:

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
            }
        });
}
mojtaba ramezani
źródło
8

Rejestruj zdarzenia zmiany trasy w następujący sposób ...

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;
                }
                }
            }
        });
    }
}
Narottam Goyal
źródło
Idealnie, jak mogę uzyskać dokładny param.id adresu URL?
ShibinRagh
6

Lokalizacja działa ...

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);
    }
}
webmaster_sean
źródło
Niezła odpowiedź. to działa w moim przypadku. Dzięki
Umar Tariq
4

powyżej większość rozwiązań jest poprawnych, ale mam do czynienia z problemem, który emituje wiele razy zdarzenie „Emituj nawigację”. kiedy zmieniłem dowolną trasę, to zdarzenie jest wyzwalane. Więc usłysz to kompletne rozwiązanie dla Angulara 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();
  }
} 
Yala Ramesh
źródło
3

Odpowiedź @Ludohen jest świetna, ale jeśli nie chcesz używać, instanceofskorzystaj z poniższych

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

w ten sposób możesz sprawdzić nazwę bieżącego zdarzenia jako ciąg znaków, a jeśli zdarzenie wystąpiło, możesz zrobić to, co zaplanowałeś.

Khaled Al-Ansari
źródło
3
dlaczego nie użyć bezpieczeństwa maszynopisu?
Pascal
@Pascal dlaczego nienawiść? a ten Eventtyp powoduje błąd w Atomie, dlatego go nie użyłem
Khaled Al-Ansari
2
@Pascal nie to problem kątowy, ponieważ zdarzenie routera nie jest tym samym co zdarzenie przeglądarki i dlatego typ zdarzenia nie działa! muszą stworzyć nowy interfejs dla tego wydarzenia, powinienem to powiedzieć od samego początku, ale nieuzasadnione głosowanie w dół nie pomogło :)
Khaled Al-Ansari
5
ponieważ minimalizacja jest wykonywana w kodzie produkcyjnym, powinieneś raczej użyć, instanceOfaby twój przykład działał również w kodzie produkcyjnym. if(event instanceOf NavigationStart) {
Milan Jaric
1
Powinien byćif(event instanceof NavigationStart)
Ketan
1

Pracuję z aplikacją angular5 i mam ten sam problem. kiedy przeglądam Dokumentację kątową, zapewniają najlepsze rozwiązanie do obsługi zdarzeń routera. sprawdź następującą dokumentację.

Reprezentuje zdarzenie wyzwalane, gdy nawigacja zakończy się pomyślnie

Jak tego użyć?

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

Kiedy tego użyć?

W moim przypadku moja aplikacja ma wspólny pulpit nawigacyjny dla wszystkich użytkowników, takich jak użytkownicy, administratorzy, ale muszę pokazać i ukryć niektóre opcje paska nawigacyjnego zgodnie z typami użytkowników.

Dlatego za każdym razem, gdy zmienia się adres URL, muszę wywołać metodę usługi, która zwraca zalogowane informacje o użytkowniku zgodnie z odpowiedzią, idę do dalszych operacji.

Bhagvat Lande
źródło
0

Poniższy RODZAJ prac i może zrobić dla ciebie podstęp.

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

Niestety przekierowuje to później w procesie routingu, niż chciałbym. TheonActivate()Oryginalnego komponentu docelowego nazywa się przed przekierowaniem.

Tam jest @CanActivate dekorator, którego można użyć na komponencie docelowym, ale jest to a) nie scentralizowane i b) nie korzysta z wstrzykiwanych usług.

Byłoby wspaniale, gdyby ktokolwiek mógł zasugerować lepszy sposób centralnego autoryzowania trasy przed jej zatwierdzeniem. Jestem pewien, że musi być lepszy sposób.

To jest mój bieżący kod (Jak mógłbym go zmienić, aby słuchać zmiany trasy?):

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: '/'})]);
Richard Evans
źródło
Widziałem, jak ludzie rozszerzają routerOutlet, aby dodać swój kod autoryzacji, co jest jednym ze sposobów. GitHub mówi o tym, ale nie ma jeszcze żadnych wniosków. Oto sposób Auth0
Dennis Smolek
Dziękuję za odpowiedź. Czy znasz jakieś dobre filmy do nauki authService for angular 2?
AngularM,
0

Tak robię od RC 5

this.router.events
  .map( event => event instanceof NavigationStart )
  .subscribe( () => {
    // TODO
  } );
jirigracik
źródło
0

Po prostu wprowadź zmiany w AppRoutingModule, takie jak

@NgModule({
imports: [RouterModule.forRoot(routes, { scrollPositionRestoration: 'enabled' })],
  exports: [RouterModule]
})
Pullat Junaid
źródło
0

Kątowy 8. Sprawdź, czy bieżąca trasa jest trasą bazową .

  baseroute: boolean;
  constructor(
    private router: Router,
  ) {
    router.events.subscribe((val: any) => {
      if (val.url == "/") {
        this.baseroute = true;
      } else {
        this.baseroute = false;
      }
    });
  }
7guyo
źródło
0

Napisałbym coś takiego:

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

ngOnDestroy() {
this.routed.unsubscribe();
}
Lalith-AIS
źródło
-3

prosta odpowiedź na Angular 8. *

constructor(private route:ActivatedRoute) {
  console.log(route);
}
er-Chaan
źródło
1
Czy nie jest to wykonywane tylko przy tworzeniu instancji, to znaczy: tylko raz !? To nie jest rozwiązanie.
Satria,