-
Notifications
You must be signed in to change notification settings - Fork 3.5k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #7537 from abpframework/feat/7524
Added a Utility Service for Getting a Stream of Angular Router Events
- Loading branch information
Showing
9 changed files
with
364 additions
and
38 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,146 @@ | ||
# Router Events Simplified | ||
|
||
`RouterEvents` is a utility service to provide an easy implementation for one of the most frequent needs in Angular templates: `TrackByFunction`. Please see [this page in Angular docs](https://angular.io/guide/template-syntax#ngfor-with-trackby) for its purpose. | ||
|
||
|
||
|
||
|
||
## Benefit | ||
|
||
You can use router events directly and filter them as seen below: | ||
|
||
```js | ||
import { | ||
NavigationEnd, | ||
NavigationError, | ||
NavigationCancel, | ||
Router, | ||
} from '@angular/router'; | ||
import { filter } from 'rxjs/operators'; | ||
|
||
@Injectable() | ||
class SomeService { | ||
navigationFinish$ = this.router.events.pipe( | ||
filter( | ||
event => | ||
event instanceof NavigationEnd || | ||
event instanceof NavigationError || | ||
event instanceof NavigationCancel, | ||
), | ||
); | ||
/* Observable<Event> */ | ||
|
||
constructor(private router: Router) {} | ||
} | ||
``` | ||
|
||
However, `RouterEvents` makes filtering router events easier. | ||
|
||
```js | ||
import { RouterEvents } from '@abp/ng.core'; | ||
|
||
@Injectable() | ||
class SomeService { | ||
navigationFinish$ = this.routerEvents.getNavigationEvents('End', 'Error', 'Cancel'); | ||
/* Observable<NavigationCancel | NavigationEnd | NavigationError> */ | ||
|
||
constructor(private routerEvents: RouterEvents) {} | ||
} | ||
``` | ||
|
||
`RouterEvents` also delivers improved type-safety. In the example above, `navigationFinish$` has inferred type of `Observable<NavigationCancel | NavigationEnd | NavigationError>` whereas it would have `Observable<Event>` when router events are filtered directly. | ||
|
||
|
||
|
||
|
||
## Usage | ||
|
||
You do not have to provide `RouterEvents` at the module or component level, because it is already **provided in root**. You can inject and start using it immediately in your components. | ||
|
||
|
||
### How to Get Specific Navigation Events | ||
|
||
You can use `getNavigationEvents` to get a stream of navigation events matching given event keys. | ||
|
||
```js | ||
import { RouterEvents } from '@abp/ng.core'; | ||
import { merge } from 'rxjs'; | ||
import { mapTo } from 'rxjs/operators'; | ||
|
||
@Injectable() | ||
class SomeService { | ||
navigationStart$ = this.routerEvents.getNavigationEvents('Start'); | ||
/* Observable<NavigationStart> */ | ||
|
||
navigationFinish$ = this.routerEvents.getNavigationEvents('End', 'Error', 'Cancel'); | ||
/* Observable<NavigationCancel | NavigationEnd | NavigationError> */ | ||
|
||
loading$ = merge( | ||
this.navigationStart$.pipe(mapTo(true)), | ||
this.navigationFinish$.pipe(mapTo(false)), | ||
); | ||
/* Observable<boolean> */ | ||
|
||
constructor(private routerEvents: RouterEvents) {} | ||
} | ||
``` | ||
|
||
|
||
### How to Get All Navigation Events | ||
|
||
You can use `getAllNavigationEvents` to get a stream of all navigation events without passing any keys. | ||
|
||
```js | ||
import { RouterEvents, NavigationStart } from '@abp/ng.core'; | ||
import { map } from 'rxjs/operators'; | ||
|
||
@Injectable() | ||
class SomeService { | ||
navigationEvent$ = this.routerEvents.getAllNavigationEvents(); | ||
/* Observable<NavigationCancel | NavigationEnd | NavigationError | NavigationStart> */ | ||
|
||
loading$ = this.navigationEvent$.pipe( | ||
map(event => event instanceof NavigationStart), | ||
); | ||
/* Observable<boolean> */ | ||
|
||
constructor(private routerEvents: RouterEvents) {} | ||
} | ||
``` | ||
|
||
|
||
### How to Get Specific Router Events | ||
|
||
You can use `getEvents` to get a stream of router events matching given event constructors. | ||
|
||
```js | ||
import { RouterEvents } from '@abp/ng.core'; | ||
import { ActivationEnd, ChildActivationEnd } from '@angular/router'; | ||
|
||
@Injectable() | ||
class SomeService { | ||
moduleActivation$ = this.routerEvents.getEvents(ActivationEnd, ChildActivationEnd); | ||
/* Observable<ActivationEnd | ChildActivationEnd> */ | ||
|
||
constructor(private routerEvents: RouterEvents) {} | ||
} | ||
``` | ||
|
||
|
||
### How to Get All Router Events | ||
|
||
You can use `getEvents` to get a stream of all router events without passing any event constructors. This is nothing different from accessing `events` property of `Router` and is added to the service just for convenience. | ||
|
||
```js | ||
import { RouterEvents } from '@abp/ng.core'; | ||
import { ActivationEnd, ChildActivationEnd } from '@angular/router'; | ||
|
||
@Injectable() | ||
class SomeService { | ||
routerEvent$ = this.routerEvents.getAllEvents(); | ||
/* Observable<Event> */ | ||
|
||
constructor(private routerEvents: RouterEvents) {} | ||
} | ||
``` | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
61 changes: 61 additions & 0 deletions
61
npm/ng-packs/packages/core/src/lib/services/router-events.service.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
import { Injectable, Type } from '@angular/core'; | ||
import { | ||
NavigationCancel, | ||
NavigationEnd, | ||
NavigationError, | ||
NavigationStart, | ||
Router, | ||
RouterEvent, | ||
} from '@angular/router'; | ||
import { filter } from 'rxjs/operators'; | ||
|
||
export const NavigationEvent = { | ||
Cancel: NavigationCancel, | ||
End: NavigationEnd, | ||
Error: NavigationError, | ||
Start: NavigationStart, | ||
}; | ||
|
||
@Injectable({ providedIn: 'root' }) | ||
export class RouterEvents { | ||
constructor(private router: Router) {} | ||
|
||
getEvents<T extends RouterEventConstructors>(...eventTypes: T) { | ||
type FilteredRouterEvent = T extends Type<infer Ctor>[] ? Ctor : never; | ||
|
||
const filterRouterEvents = (event: RouterEvent): event is FilteredRouterEvent => | ||
eventTypes.some(type => event instanceof type); | ||
|
||
return this.router.events.pipe(filter(filterRouterEvents)); | ||
} | ||
|
||
getNavigationEvents<T extends NavigationEventKeys>(...navigationEventKeys: T) { | ||
type FilteredNavigationEvent = T extends (infer Key)[] | ||
? Key extends NavigationEventKey | ||
? InstanceType<NavigationEventType[Key]> | ||
: never | ||
: never; | ||
|
||
const filterNavigationEvents = (event: RouterEvent): event is FilteredNavigationEvent => | ||
navigationEventKeys.some(key => event instanceof NavigationEvent[key]); | ||
|
||
return this.router.events.pipe(filter(filterNavigationEvents)); | ||
} | ||
|
||
getAllEvents() { | ||
return this.router.events; | ||
} | ||
|
||
getAllNavigationEvents() { | ||
const keys = Object.keys(NavigationEvent) as NavigationEventKeys; | ||
return this.getNavigationEvents(...keys); | ||
} | ||
} | ||
|
||
type RouterEventConstructors = [Type<RouterEvent>, ...Type<RouterEvent>[]]; | ||
|
||
type NavigationEventKeys = [NavigationEventKey, ...NavigationEventKey[]]; | ||
|
||
type NavigationEventType = typeof NavigationEvent; | ||
|
||
export type NavigationEventKey = keyof NavigationEventType; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.