จะตรวจจับการเปลี่ยนเส้นทางใน Angular ได้อย่างไร


428

AppComponentฉันกำลังมองหาในการตรวจสอบการเปลี่ยนแปลงในเส้นทางของฉัน

หลังจากนั้นฉันจะตรวจสอบโทเค็นผู้ใช้ทั่วโลกเพื่อดูว่าเขาเข้าสู่ระบบแล้วฉันสามารถเปลี่ยนเส้นทางผู้ใช้ถ้าเขาไม่ได้เข้าสู่ระบบ

คำตอบ:


534

ใน Angular 2 คุณสามารถsubscribe(เหตุการณ์ Rx) ไปยังอินสแตนซ์เราเตอร์ ดังนั้นคุณสามารถทำสิ่งต่าง ๆ เช่น

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หวังว่าจะมีวิธีที่ดีกว่านี้
Akshay

6
@Akshay คุณได้เห็นบทความนี้โดยทอดด์ภาษิต: [ไดนามิกชื่อหน้าในเชิงมุม 2 กับเหตุการณ์ที่เราเตอร์] ( toddmotto.com/dynamic-page-titles-angular-2-router-events )
Bogac

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 แทน 'คือ' ฉันจะใช้ 'instanceof' และ 'เหตุการณ์: กิจกรรม' ควรอยู่ในวงเล็บ ขอบคุณสำหรับคุณลักษณะใหม่ที่ทรงพลัง! ฉันชอบมัน
Maxim

2
สิ่งนี้ทำให้เกิดข้อผิดพลาดในการคอมไพล์ในเวอร์ชันปัจจุบันArgument of type '(event: Event) => void' is not assignable to parameter of type
Rudi Strydom

1
@RudiStrydom & GünterZöchbauer - Argument of type '(event: Event) => void' is not assignable to parameter of typeข้อผิดพลาดเป็นเพราะในตัวอย่างตัวกรองของคุณคุณสมัครเป็นวัตถุประเภทเหตุการณ์แทน NavigationEvent
Bonnici

1
ตัวอย่างที่สองควรเป็น NavigationEvent แทนกิจกรรม นอกจากนี้อย่าลืมที่จะนำเข้า "Event as NavigationEvent" จาก @ angular / router
Mick

1
คำแนะนำเกี่ยวกับการนำเข้าเป็นสำหรับคนที่ต้องการที่จะแก้ปัญหาข้อผิดพลาดนี้ :)
มิก

91

สำหรับคนที่มีมุม 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 แบ็กเอนด์ถ้าทั้งหมด
Phil

5
ในConstructorคุณไม่ควรใช้ <this> นี้เคสของคุณใช้สำหรับ ngOnInit
Sergio Reis

1
สมบูรณ์แบบฉันจะได้รับพารามิเตอร์ที่แน่นอนจาก 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-beta.2 ควรเป็น

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

ใช้งานได้กับเส้นทางปัจจุบัน แต่ก่อนหน้านี้เป็นอย่างไร
tatsu

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"
Damir Beylkhanov

15

router-deprecatedคำตอบที่นี่เป็นที่ถูกต้องสำหรับ สำหรับเวอร์ชั่นล่าสุดrouter:

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

หรือ

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

หากต้องการดูความแตกต่างระหว่างทั้งสองโปรดอ่านคำถาม SOนี้

แก้ไข

สำหรับล่าสุดคุณต้องทำ:

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

มันให้ข้อมูลใด ๆ ของเส้นทางก่อนหน้าและปัจจุบันหรือไม่?
akn

routerได้รับการปรับปรุงใหม่อีกครั้ง (ผมยังไม่ได้รับการปรับปรุงคำตอบของฉันเลย) ดังนั้นผมไม่แน่ใจว่ามันคือสำหรับล่าสุด สำหรับrouterฉันเขียนถึงคุณทำไม่ได้ @akn
Dehli

คุณช่วยอธิบายบริบทสำหรับคำตอบนี้ได้ไหม? คุณเปลี่ยนสายงานใดในโซลูชันอื่นด้วยตัวคุณ
เจอราร์ดซิมป์สัน

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

สมบูรณ์แบบฉันจะได้รับพารามิเตอร์ที่แน่นอนจาก 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);
    }
}

คำตอบที่ดี มันใช้งานได้กับกรณีของฉัน ขอบคุณ
Umar Tariq

4

เหนือโซลูชันส่วนใหญ่ถูกต้อง แต่ฉันกำลังเผชิญหน้ากับปัญหานี้ปล่อยเหตุการณ์ 'นำทางปล่อย' หลายครั้งเมื่อฉันเปลี่ยนเส้นทางใด ๆ กิจกรรมนี้จะถูกเรียก ดังนั้นการได้ยินเป็นทางออกที่สมบูรณ์สำหรับ 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
ทำไมไม่ใช้ typescript safety?
Pascal

@ ปาสกาลทำไมถึงเกลียด? และEventประเภทที่ทำให้เกิดข้อผิดพลาดใน Atom นั่นคือเหตุผลที่ฉันไม่ได้ใช้มัน
Khaled Al-Ansari

2
@Pascal ไม่ใช่ปัญหาเชิงมุมเนื่องจากเหตุการณ์เราเตอร์ไม่เหมือนกับเหตุการณ์ในเบราว์เซอร์และนั่นเป็นสาเหตุที่ประเภทกิจกรรมไม่ทำงาน! พวกเขาจำเป็นต้องสร้างอินเทอร์เฟซใหม่สำหรับเหตุการณ์นี้ฉันควรจะบอกว่าตั้งแต่เริ่มต้น แต่การโหวตลงอย่างไม่มีเหตุผลไม่ได้ช่วย :)
Khaled Al-Ansari

5
เนื่องจากมีการดำเนินการลดขนาดในรหัสการผลิตคุณควรใช้instanceOfดังนั้นตัวอย่างของคุณจะทำงานในรหัสการผลิตด้วย if(event instanceOf NavigationStart) {
มิลานจาริก

1
ควรเป็นif(event instanceof NavigationStart)
Ketan

1

ฉันกำลังทำงานกับแอปพลิเคชัน angular5 และฉันกำลังเผชิญกับปัญหาเดียวกัน เมื่อฉันอ่านเอกสารเชิงมุมพวกเขาให้ทางออกที่ดีที่สุดสำหรับการจัดการเราเตอร์ events.check เอกสารดังต่อไปนี้

แสดงให้เห็นถึงเหตุการณ์ที่เกิดขึ้นเมื่อการนำทางจบลงด้วยความสำเร็จ

ใช้อย่างไร

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

จะใช้เมื่อไหร่?

ในกรณีของฉันแอปพลิเคชันของฉันจะแบ่งปันแดชบอร์ดทั่วไปสำหรับผู้ใช้ทั้งหมดเช่นผู้ใช้ผู้ดูแลระบบ แต่ฉันต้องแสดงและซ่อนตัวเลือก navbar บางตัวตามประเภทผู้ใช้

นั่นเป็นเหตุผลที่เมื่อใดก็ตามที่การเปลี่ยนแปลง 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มัณฑนากรที่คุณสามารถใช้กับองค์ประกอบเป้าหมาย แต่นี่คือ a) ไม่รวมศูนย์และ b) ไม่ได้รับประโยชน์จากบริการที่ถูกฉีด

มันจะดีถ้าใครสามารถแนะนำวิธีที่ดีกว่าในการอนุมัติเส้นทางก่อนที่มันจะถูกส่งไป ฉันแน่ใจว่าจะต้องมีวิธีที่ดีกว่า

นี่คือรหัสปัจจุบันของฉัน (ฉันจะเปลี่ยนเพื่อฟังการเปลี่ยนเส้นทางได้อย่างไร):

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
Dennis Smolek

ขอขอบคุณสำหรับการตอบสนองของคุณ. คุณรู้วิดีโอที่ดีสำหรับการเรียนรู้ 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

เชิงมุม 8. ตรวจสอบว่าเส้นทางปัจจุบันเป็นเส้นทางพื้นฐานหรือไม่

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


-3

คำตอบง่ายๆสำหรับ Angular 8 *

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

1
นี่ไม่ใช่แค่การดำเนินการในการสร้างอินสแตนซ์เท่านั้นซึ่งจะเป็น: เพียงครั้งเดียวเท่านั้น! นี่ไม่ใช่วิธีแก้ปัญหา
Satria
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.