Advanced Routing
Routing essentials
Implement Feature-Level-Routing and Aux-Routing like seen in the live coding, if you like.
CanActivateGuard
In this exercise, you will create an AuthService that users can use to log in. This AuthService remembers the current user name and whether the user is logged in. The possibilities of the service should be offered to the user in the HomeComponent.
In addition, create a CanActivate-Guard named AuthGuard, which only allows a route change if the current user is logged in. For this it relies on the AuthService:
[HomeComponent] -------> [AuthService]
^
|
[AuthGuard]
You can use the following procedure:
-
Create a
authfolder in the project'ssharedfolder. -
Create an
AuthServicein the new folderauth:@Injectable({ providedIn: 'root' }) export class AuthService { userName: string; constructor() { } login() { this.userName = 'Max'; } logout() { this.userName = null; } } -
Create a
auth.guard.tsfile in the same folder with aCanActivateguard based onAuthService.Show code
@Injectable({ providedIn: 'root' }) export class AuthGuard implements CanActivate { constructor(private router: Router, private authService: AuthService) { } canActivate() { if (this.authService.userName) { return true; } return this.router.navigate(['/home', { needsLogin: true }]) } } -
Make sure the
AppModulelocated inapp.module.tsimportsSharedModule.Show code
@NgModule({ imports: [ [...] SharedModule.forRoot() ], [...] }) export class AppModule { } -
Open the file
home.component.tsand have theAuthServiceinjected there. Wrap its functionality with methods or setters.Show code
[...] export class HomeComponent implements OnInit { constructor([...], private authService: AuthService) { } ngOnInit() { } get userName() { return this.authService.userName; } login() { this.authService.login(); } logout() { this.authService.logout(); } } -
Open the file
home.component.html. Display the current user name.Show code
<h1 *ngIf="userName">Welcome, {{userName}}!</h1> <h1 *ngIf="!userName">Welcome!</h1> -
Run the application and check if the implementation works.
-
Register the
AuthGuardin the route configuration inflight-booking.routes.tsto protect one of the established routes.Show code
export const FLIGHT_BOOKING_ROUTES: Routes = [ { path: 'flight-search', component: FlightSearchComponent, canActivate: [AuthGuard], [...] } [...] ]; -
Test your solution.
CanDeactivateGuard
In this exercise, you will develop a CanDeactivate-Guard to warn the user before leaving a route.
-
Create a
deactivationfolder in thesharedfolder. -
Create a file
can-deactivate.guard.tsin the new folderdeactivation:export interface CanDeactivateComponent { canDeactivate(): Observable<boolean>; } @Injectable({ providedIn: 'root' }) export class CanDeactivateGuard implements CanDeactivate<CanDeactivateComponent> { canDeactivate( component: CanDeactivateComponent, currentRoute: ActivatedRouteSnapshot, currentState: RouterStateSnapshot, nextState?: RouterStateSnapshot): Observable<boolean> { return component.canDeactivate(); } }
The interface CanDeactivateComponent is used here as an abstraction for the components to be used with the Guard.
-
Open the file
flight-edit.component.tsand implement there the interfaceCanDeactivateComponentso that on exit one flag is set and on the other hand anObservable<boolean>is returned. The flag causes a warning message to be displayed. Once the user has announced that we really want to leave the route, we want to sendtrueorfalseto the router via the observable. After that it has to be closed. In addition, the flag should be reset afterwards.Show code
export class FlightEditComponent implements OnInit, CanDeactivateComponent { [...] sender: Observer<boolean>; decide(decision: boolean): void { this.showWarning = false; this.sender.next(decision); this.sender.complete(); } canDeactivate(): Observable<boolean> { return new Observable((sender: Observer<boolean>) => { this.sender = sender; this.showWarning = true; }); } [...] } -
The file
flight-edit.component.htmlalready contains a warning box. It should present two possible strings (yesandno) to the user. -
Also register the Guard in the file
flight-booking.routes.ts:Show code
[...] { path: 'flight-edit/:id', component: FlightEditComponent, canDeactivate: [CanDeactivateGuard] }, [...] -
Test your solution.
Lazy Loading
Implementing Lazy Loading for a feature module
Implement lazy loading for the FlightBookingModule in your app.routes.ts.
Keep in mind that lazy loading only works if the module in question isn't referenced directly but only with a string in the router configuration.
-
Open the file
app.module.tsand remove the import for theFlightBookingModule.Show Code
@NgModule({ imports: [ [...] // FlightBookingModule, // ^^ Removed b/c this would prevent lazy loading [...] ], [...] }) export class AppModule { } -
Since Angular 8, we are using EcmaScript inline imports for lazy loading. To make them work, you have to adjust your
tsconfig.app.json(inflight-app):- Here, make sure,
moduleis set toesnext.
- Here, make sure,
-
Open the file
app.routes.tsand introduce a route with the pathflight-booking. It should point to theFlightBookingModuleusingloadChildren:Show Code
[...] { path: 'flight-booking', loadChildren: () => import('./flight-booking/flight-booking.module').then(m => m.FlightBookingModule) }, { // This route needs to be the last one! path: '**', [...] } [...] -
Open the file
flight-booking.routes.tsand change the path for the first route to an empty string (path: '') to make this route the default route that is activated after lazy loading the module.Show Code
const FLIGHT_BOOKING_ROUTES: Routes = [ { path: '', component: FlightBookingComponent, [...], children: [ [...] ] } ]; [...] -
Find out that webpack splits off an own chunk for the
FlightBookingModuleafter implementing lazy loading. If this works, you will see another chunk at the console (e. g.flight-booking-flight-booking-module.jsdepending on the used version of the CLI) -
Try it out in the browser and use the network tab within the dev tools (F12) to make sure that it is only loaded on demand. If it doesn't work, have a look to the console tab within the dev tools.
Implementing Preloading
In this exercise you will implement Preloading using Angular's PreloadAllModules strategy.
-
Open the file
app.module.tsand register thePreloadAllModulesstrategy when callingRouterModule.forRoot.Show Code
RouterModule.forRoot(APP_ROUTES, { preloadingStrategy: PreloadAllModules }); -
Make sure it works using the network tab within Chrome's dev tools. If it works, the lazy bundles are loaded after the app has been initializes. If this is the case, the chunks show up quite late in the water fall diagram.