Wie erkenne ich eine Routenänderung in Angular?


426

Ich suche nach einer Routenänderung in meinem AppComponent.

Danach überprüfe ich das globale Benutzertoken, um festzustellen, ob er angemeldet ist. Dann kann ich den Benutzer umleiten, wenn er nicht angemeldet ist.

Antworten:


534

In Angular 2 können Sie subscribe(Rx-Ereignis) zu einer Router-Instanz. So können Sie Dinge wie tun

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

Bearbeiten (seit rc.1)

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

Bearbeiten 2 (seit 2.0.0)

Siehe auch: Router.events doc

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

2
Ich bin in der Lage, Daten zu erhalten, indem event._root.children[0].value._routeConfig.dataich hoffe, dass es einen besseren Weg gibt
Akshay

6
@Akshay haben Sie diesen Artikel von Todd Motto gesehen: [Dynamische Seitentitel in Angular 2 mit Router-Ereignissen] ( toddmotto.com/dynamic-page-titles-angular-2-router-events )
Bogac

9
warum feuert es 3 mal?
Toolkit

2
@Toolkit ist, weil das Ereignis 3 Zustände hat und in einer erfolgreichen Änderung der URL. Die 3 Zustände sind: 'NavigationStart', 'NavigationEnd' und 'RoutesRecognized'
RicardoGonzales

10
Sie können die Ereignisse einfach mit dem RxJS- filterOperator filtern . router.events.pipe(filter(e => e instanceof NavigationEnd).subscribe((e) => { ... }
Simon_Weaver

313

RxJS 6

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

Vielen Dank an Peilonrayz (siehe Kommentare unten)

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

Sie können auch nach dem angegebenen Ereignis filtern:

import 'rxjs/add/operator/filter';

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

Es ist auch eine gute Idee, den pairwiseOperator zu verwenden, um das vorherige und aktuelle Ereignis abzurufen. 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 anstelle von 'is' würde ich 'instanceof' verwenden. Und 'event: Event' sollte in Klammern stehen. Vielen Dank für diese ziemlich leistungsstarke neue Funktion! Ich mag es
Maxim

2
Dies wirft einen Kompilierungsfehler auf die aktuellen VersionenArgument of type '(event: Event) => void' is not assignable to parameter of type
Rudi Strydom

1
@RudiStrydom & Günter Zöchbauer - Der Argument of type '(event: Event) => void' is not assignable to parameter of typeFehler liegt darin, dass Sie in Ihrem Filter-Snippet ein Objekt vom Typ Event anstelle von NavigationEvent abonnieren.
Bonnici

1
Das zweite Beispiel sollte NavigationEvent anstelle von Event sein. Vergessen Sie auch nicht, "Event as NavigationEvent" von @ angle / router zu importieren
Mick

1
Der Hinweis zum Import war für alle, die diesen Fehler beheben möchten :)
Mick

91

Für Angular 7 sollte jemand schreiben wie:

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


Ein detailliertes Beispiel kann wie folgt sein:

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
Das ist toll! sehr umfassend! funktionierte perfekt auf Angular 7.
MaylorTaylor

1
Normalerweise nimmt die Navigation selbst sehr wenig Zeit in Anspruch, wenn Sie eine Vorladestrategie verwenden. In Bezug auf die Benutzerfreundlichkeit würde ich Ladeindikatoren, wenn überhaupt, nur für http-Anfragen im Backend verwenden.
Phil

5
Im Konstruktor sollten Sie <this> nicht verwenden, Ihr Fall ist für ngOnInit.
Sergio Reis

1
Perfekt, wie kann ich die genaue Parameter-ID der URL erhalten?
ShibinRagh

2
Die Lösung ist nicht auf Komponenten beschränkt, sondern verbreitet sich in der gesamten App und verbraucht Ressourcen
Md. Rafee

54

Angular 7 , wollen , wenn Sie subscribezurouter

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
Das Umleitungsereignis wird nicht erfasst
Anandhu Ajayakumar,

40

Winkel 4.x und höher:

Dies kann mithilfe der URL-Eigenschaft der ActivatedRoute- Klasse wie folgt erreicht werden:

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

Hinweis: Sie müssen den Anbieter aus dem angular/routerPaket importieren und injizieren

import { ActivatedRoute } from '@angular/router`

und

constructor(private activatedRoute : ActivatedRoute){  }

18

Router 3.0.0-beta.2 sollte sein

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

das funktioniert für den aktuellen Pfad, aber was ist mit dem vorherigen?
Tatsu

16

In Winkel 6 und 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
Sie haben den Import für Router verpasstimport {Router, NavigationEnd} from "@angular/router"
Damir Beylkhanov

15

Die Antworten hier sind richtig für router-deprecated. Für die neueste Version von router:

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

oder

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

Um den Unterschied zwischen den beiden zu sehen, lesen Sie bitte diese SO-Frage .

Bearbeiten

Für das Neueste müssen Sie Folgendes tun:

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

Gibt es Daten zur vorherigen und aktuellen Route?
Akn

Das routerwurde erneut aktualisiert (ich habe meine Antwort noch nicht aktualisiert), daher bin ich mir nicht sicher, wie es für die neueste Version ist. Für das router, worüber ich schrieb, konnte man nicht. @akn
Dehli

Könnten Sie bitte einen Kontext für diese Antwort angeben? Welche Zeilen ersetzen Sie in den anderen Lösungen durch Ihre?
Gerard Simpson

12

In Angular 8 sollten Sie mögen this.router.events.subscribe((event: Event) => {})

Beispiel:

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

In der Komponente möchten Sie möglicherweise Folgendes versuchen:

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

Erfassen Sie Routenänderungsereignisse auf folgende Weise ...

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

Perfekt, wie kann ich die genaue Parameter-ID der URL erhalten?
ShibinRagh

6

Ort funktioniert ...

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

Gute Antwort. es funktioniert für meinen Fall. Danke
Umar Tariq

4

Die meisten Lösungen sind korrekt, aber ich habe das Problem, dass dieses Ereignis mehrmals "Navigation emittieren" ausgelöst wird. Wenn ich eine Route geändert habe, wird dieses Ereignis ausgelöst. Hören ist also die Komplettlösung für 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 Antwort ist großartig, aber falls Sie nicht verwenden möchten, instanceofverwenden Sie die folgenden

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

Auf diese Weise können Sie den aktuellen Ereignisnamen als Zeichenfolge überprüfen und bei Auftreten des Ereignisses das tun, wofür Sie Ihre Funktion geplant haben.


3
Warum nicht Typoskript-Sicherheit verwenden?
Pascal

@Pascal warum der Hass? und der EventTyp verursacht einen Fehler in Atom, deshalb habe ich ihn nicht verwendet
Khaled Al-Ansari

2
@Pascal nein, es ist ein Angular-Problem, da das Router-Ereignis nicht mit dem Browser-Ereignis identisch ist und der Ereignistyp daher nicht funktioniert! Sie müssen eine neue Schnittstelle für dieses Ereignis erstellen, ich hätte sagen sollen, dass von Anfang an, aber die unvernünftige Abstimmung nicht geholfen hat :)
Khaled Al-Ansari

5
Da die Minimierung im Produktionscode durchgeführt wird, sollten Sie sie lieber verwenden, instanceOfdamit Ihr Beispiel auch im Produktionscode funktioniert. if(event instanceOf NavigationStart) {
Milan Jaric

1
Sollte seinif(event instanceof NavigationStart)
Ketan

1

Ich arbeite mit der Angular5-Anwendung und stehe vor dem gleichen Problem. Wenn ich Angular Documentation durchlaufe, bieten sie die beste Lösung für die Behandlung von Router-Ereignissen. Überprüfen Sie die folgende Dokumentation.

Stellt ein Ereignis dar, das ausgelöst wird, wenn eine Navigation erfolgreich beendet wird

Wie benutzt man das?

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

Wann soll man das benutzen?

In meinem Fall hat meine Anwendung ein gemeinsames Dashboard für alle Benutzer wie Benutzer und Administratoren, aber ich muss einige Navigationsleistenoptionen gemäß Benutzertypen anzeigen und ausblenden.

Das ist der Grund, warum ich, wenn sich die URL ändert, die Dienstmethode aufrufen muss, die die angemeldeten Benutzerinformationen gemäß der Antwort zurückgibt, weitere Vorgänge ausführen werde.


0

Die folgende Art von Arbeiten und kann das Knifflige für Sie tun.

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

Leider leitet dies später im Routing-Prozess weiter, als ich möchte. Die onActivate()der ursprünglichen Zielkomponente wird vor der Umleitung aufgerufen.

Es gibt einen @CanActivateDekorator, den Sie für die Zielkomponente verwenden können, der jedoch a) nicht zentralisiert ist und b) nicht von injizierten Diensten profitiert.

Es wäre großartig, wenn jemand eine bessere Möglichkeit vorschlagen könnte, eine Route zentral zu autorisieren, bevor sie festgeschrieben wird. Ich bin sicher, es muss einen besseren Weg geben.

Dies ist mein aktueller Code (Wie würde ich ihn ändern, um die Routenänderung zu hören?):

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

Ich habe gesehen, wie Leute das routerOutlet erweitert haben, um ihren Authentifizierungscode hinzuzufügen, was eine Möglichkeit ist. Auf gitHub wird darüber gesprochen, aber noch keine Schlussfolgerung. Hier ist Auth0s Weg: auth0.com/blog/2015/05/14/…
Dennis Smolek

Danke für Ihre Antwort. Kennen Sie gute Videos zum Erlernen des authService für Angular 2?
AngularM

0

Ich mache es so seit RC 5

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

0

Nehmen Sie einfach Änderungen an AppRoutingModule vor

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

0

Winkel 8. Überprüfen Sie, ob die aktuelle Route die Basisroute ist .

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

0

Ich würde so etwas schreiben:

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

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

-3

einfache Antwort für Angular 8. *

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

1
Wird dies nicht nur bei der Instanziierung ausgeführt, das heißt: nur einmal!? Dies ist keine Lösung.
Satria
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.