Hasta ahora solo tenemos una vista en nuestra pequeña aplicación Angular. Por supuesto, vamos a querer tener más opciones para que el usuario visite diferentes vistas para obtener información y experiencias diferentes. Al igual que tenemos enlaces en páginas web básicas, también podemos configurar enlaces en una aplicación Angular para navegar a varias vistas. Es a través de la funcionalidad de enrutamiento proporcionada en Angular que podemos configurar varias rutas a varios conjuntos de información. En este tutorial, aprenderemos cómo funciona el enrutamiento, cómo configurar rutas, cómo vincular rutas a acciones y cómo construir las distintas vistas que necesitaremos.
Crear un nuevo componente
En este momento, hay un componente de lista de juegos simple que muestra una lista de todos los juegos en la aplicación. Ahora, queremos construir un nuevo componente que muestre los detalles de un juego a la vez. Querremos poder hacer clic en un enlace y que funcione el enrutamiento angular para que se nos envíe a una vista detallada de ese juego. En otras palabras, necesitamos un nuevo componente para poder dirigirnos a ese componente. Así es como se verá el componente de detalles del juego.
El componente de detalles del juego consta de game-detail.css, game-detail.component.html y game-detail.component.ts. Viven en la games
carpeta. Aquí están esos archivos.
game-detail.css
| .col-md-4 { display: flex; align-items: center; justify-content: center; } |
game-detail.component.html
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | <div class='card' *ngIf='game'> <div class='card-header'> {{pageTitle + ': ' + game.gameName}} </div> <div class='card-body'> <div class='row'> <div class='col-md-4'> <img class='center-block img-responsive' [style.width.px]='200' [src]='game.imageUrl' [title]='game.gameName'> </div> <div class='col-md-8'> <h5 class="card-title">{{game.gameName}}</h5> <p class="card-text">{{game.description}}</p> <ul class="list-group"> <li class="list-group-item"><b>Part#:</b> {{game.gameCode | lowercase | convertToColon: '-'}}</li> <li class="list-group-item"><b>Released:</b> {{game.releaseDate}}</li> <li class="list-group-item"><b>Cost:</b> {{game.price|currency:'USD':'symbol'}}</li> <li class="list-group-item"><b>Rating:</b> <game-thumb [rating]='game.thumbRating'></game-thumb> </li> </ul> </div> </div> </div> <div class='card-footer'> <button class='btn btn-outline-secondary' (click)='onBack()'> <i class='fa fa-chevron-left'></i> Back </button> </div> </div> |
game-detail.component.ts
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 | import {Component, OnInit} from '@angular/core'; import {ActivatedRoute, Router} from '@angular/router'; import {IGame} from './game'; import {GameService} from './game.service'; @Component({ templateUrl: './game-detail.component.html', styleUrls: ['./game-detail.component.css'] }) export class GameDetailComponent implements OnInit { pageTitle = 'Game Detail'; errorMessage = ''; game: IGame | undefined; constructor(private route: ActivatedRoute, private router: Router, private gameService: GameService) { } ngOnInit() { const param = this.route.snapshot.paramMap.get('id'); if (param) { const id = +param; this.getGame(id); } } getGame(id: number) { this.gameService.getGame(id).subscribe( game => this.game = game, error => this.errorMessage = <any>error); } onBack(): void { this.router.navigate(['/games']); } } |
Cómo funciona el enrutamiento angular
Todas las vistas se muestran dentro de una página en una aplicación angular. Esto es lo que se conoce como aplicación de página única o SPA . La página donde se muestran todas las vistas suele ser el archivo index.html . El nuestro se ve así.
| <!doctype html> <html lang="en"> <head> <meta charset="utf-8"> <title>Classic Games</title> <base href="/"> <meta name="viewport" content="width=device-width, initial-scale=1"> <link rel="icon" type="image/x-icon" href="favicon.ico"> </head> <body> <game-root></game-root> </body> </html> |
Se puede mostrar cualquier número de páginas o vistas en la página de índice en cualquier momento. Puede tener tres vistas o cientos. Cualquiera de ellos podría configurarse para mostrarse en el archivo index.html. ¿Como sucedió esto? Esto sucede usando Routing In Angular. Para configurar el enrutamiento en Angular, puede seguir estos pasos.
- Configurar una ruta para cada componente
- Definir las opciones o acciones para el usuario.
- Vincular una ruta a una opción o acción
- Activar la ruta según la acción del usuario (clics)
- Cuando se activa una ruta, se muestra la vista de componentes
En la captura de pantalla siguiente hay un menú de muestra que proporciona las opciones a un usuario de la aplicación. Una ruta está vinculada a la opción del menú (los enlaces) para que Angular pueda enrutar a un determinado componente. Esto se logra con la directiva de enrutador incorporado de routerLink
. Cuando el usuario hace clic en la opción Juegos, y el enrutador angular navega a la ruta / juegos. Esto actualiza la URL del navegador y carga el componente deseado en la vista.
Por lo tanto, cuando el usuario visita http: // localhost: 4200 / games en la barra de direcciones, ve la lista de juegos.
Configurar rutas
Veamos cómo configurar realmente algunas rutas en Angular. El enrutamiento se basa en componentes y, por lo tanto, el flujo de trabajo debe determinar primero qué componente (s) le gustaría tener como destino de enrutamiento. Luego, puede definir la (s) ruta (s) según sea necesario. Primero, necesitaremos un componente de página de inicio, ya que ahora se mostrará de forma predeterminada en lugar de la lista de juegos. Este componente vive en una home
carpeta y consta de homepage.component.html y homepage.component.ts.
homepage.component.html
| <div class="card"> <div class="card-header"> {{pageTitle}} </div> <div class="card-body"> <div class="container-fluid"> <div class="text-center"> <img src="./assets/images/classic-games.png" class="img-responsive center-block"/> </div> </div> </div> <div class="card-footer text-muted text-right"> <small>Powered by Angular</small> </div> </div> |
homepage.component.ts
| import { Component } from '@angular/core'; @Component({ templateUrl: './homepage.component.html' }) export class HomepageComponent { public pageTitle = 'Homepage'; } |
En el archivo app.module.ts , ahora podemos especificar este componente como la ruta predeterminada, por así decirlo, usando RouterModule.forRoot()
.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | import {BrowserModule} from '@angular/platform-browser'; import {NgModule} from '@angular/core'; import {HttpClientModule} from '@angular/common/http'; import {RouterModule} from '@angular/router'; import {AppComponent} from './app.component'; import {HomepageComponent} from './home/homepage.component'; import {GameModule} from './games/game.module'; @NgModule({ declarations: [ AppComponent, HomepageComponent ], imports: [ BrowserModule, HttpClientModule, RouterModule.forRoot([ {path: 'home', component: HomepageComponent}, {path: '', redirectTo: 'home', pathMatch: 'full'}, {path: '**', redirectTo: 'home', pathMatch: 'full'} ]), GameModule ], bootstrap: [AppComponent] }) export class AppModule { } |
Es en el archivo app.component.ts donde podemos configurar las opciones de routerLink para el usuario. De hecho, usaremos la routerLinkActive
directiva para que cuando un usuario navegue a una ruta específica, ese botón o enlace aparezca resaltado. También tenga en cuenta el uso del elemento especial de salida del enrutador, que es un marcador de posición que Angular llena dinámicamente en función del estado actual del enrutador. En otras palabras, cada vez que se activa una ruta, la vista asociada con esa ruta se carga en la etiqueta <router-outlet> </router-outlet> para su visualización.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | import {Component} from '@angular/core'; @Component({ selector: 'game-root', template: ` <div class="container"> <nav class='navbar navbar-expand-lg'> <ul class='nav nav-pills mr-auto'> <li><a class='nav-link' routerLinkActive='active' [routerLink]="['/home']">Home</a></li> <li><a class='nav-link' routerLinkActive='active' [routerLink]="['/games']">Games</a></li> </ul> <span class="navbar-text">{{pageTitle}}</span> </nav> <div class='container'> <router-outlet></router-outlet> </div> </div>`, styleUrls: ['./app.component.css'] }) export class AppComponent { pageTitle = 'Angular Games Viewer'; } |
Pasar parámetros a una ruta
Para enrutar a elementos específicos de una colección, puede pasar parámetros a una ruta. Por ejemplo, todos los juegos de nuestra aplicación tienen una identificación asociada. Para dirigirnos a un juego específico, necesitaríamos especificar de alguna manera la identificación del juego en la ruta. Por ejemplo, visitar la ruta http: // localhost: 4200 / games / 5 muestra un juego específico.
game-list.component.html
En nuestro componente de lista de juegos, donde enumeramos todos los juegos, podemos configurar el paso de parámetros de ruta como se ve con el código resaltado a continuación.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 | <div class='card'> <div class='card-header'> {{pageTitle}} </div> <div class='card-body'> <div class="row"> <div class="col-3"> <input type="text" [(ngModel)]='listFilter' class="form-control" id="filterInput" placeholder="Type to filter"> </div> <div class="col"> <div *ngIf='listFilter' class="form-text text-muted">Filtered by: {{listFilter}}</div> </div> </div> <div class='table-responsive'> <table class='table' *ngIf='games && games.length'> <thead> <tr> <th> <button class='btn btn-primary' (click)='toggleImage()'> {{showImage ? 'Hide ' : 'Show'}} Image </button> </th> <th>Game</th> <th>Part#</th> <th>Release Date</th> <th>Cost</th> <th>5 Thumb Rating</th> </tr> </thead> <tbody> <tr *ngFor='let game of filteredGames'> <td> <img *ngIf='showImage' [src]='game.imageUrl' [title]='game.gameName' [style.width.px]='imageWidth' [style.margin.px]='imageMargin'> </td> <td> <a [routerLink]="['/games', game.gameId]"> {{ game.gameName }} </a> </td> <td>{{ game.gameCode | lowercase | convertToColon: '-' }}</td> <td>{{ game.releaseDate }}</td> <td>{{ game.price | currency:'USD':'symbol':'1.2-2' }}</td> <td> <game-thumb [rating]='game.thumbRating' (ratingClicked)='onRatingClicked($event)'> </game-thumb> </td> </tr> </tbody> </table> </div> </div> <div class="card-footer text-muted text-right"> <small>Powered by Angular</small> </div> </div> <div *ngIf='errorMessage' class='alert alert-danger'> Error: {{ errorMessage }} </div> |
Definir rutas con parámetros
Ok, los enlaces están en el lugar indicado arriba usando el enlace en el formato de <a [routerLink]="['/games', game.gameId]”>. Tenga en cuenta que la ruta es / games y después de la coma pasamos el id del juego que queremos ver. Para que esto funcione, las rutas deben definirse en algún lugar de la aplicación. Para estos enlaces, podemos definir las rutas en el game.module.ts
archivo usando RouterModule.forChild () .
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | import {NgModule} from '@angular/core'; import {RouterModule} from '@angular/router'; import {GameListComponent} from './game-list.component'; import {GameDetailComponent} from './game-detail.component'; import {ConvertToColonPipe} from '../shared/convert-to-colon.pipe'; import {GameDetailGuard} from './game-detail.guard'; import {SharedModule} from '../shared/shared.module'; @NgModule({ imports: [ RouterModule.forChild([ {path: 'games', component: GameListComponent}, { path: 'games/:id', canActivate: [GameDetailGuard], component: GameDetailComponent }, ]), SharedModule ], declarations: [ GameListComponent, GameDetailComponent, ConvertToColonPipe ] }) export class GameModule { } |
El archivo game-detail.component.ts también debe conocer la ruta. Para mostrar el juego correcto, el componente de detalles del juego lee el parámetro de la URL. Luego, ese parámetro se usa para buscar el juego correcto de la API de back-end que habíamos configurado en un tutorial anterior. Para obtener el parámetro de la URL, se utiliza el servicio ActivatedRoute . Esto se configura como una dependencia en el constructor.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 | import {Component, OnInit} from '@angular/core'; import {ActivatedRoute, Router} from '@angular/router'; import {IGame} from './game'; import {GameService} from './game.service'; @Component({ templateUrl: './game-detail.component.html', styleUrls: ['./game-detail.component.css'] }) export class GameDetailComponent implements OnInit { pageTitle = 'Game Detail'; errorMessage = ''; game: IGame | undefined; constructor(private route: ActivatedRoute, private router: Router, private gameService: GameService) { } ngOnInit() { const param = this.route.snapshot.paramMap.get('id'); if (param) { const id = +param; this.getGame(id); } } getGame(id: number) { this.gameService.getGame(id).subscribe( game => this.game = game, error => this.errorMessage = <any>error); } onBack(): void { this.router.navigate(['/games']); } } |
¡Probar las rutas detalladas del juego en el navegador está funcionando bastante bien!
Activar rutas con código
Es posible que haya notado el botón Atrás que nos lleva de regreso a la vista de componentes de la lista de juegos. Esta ruta se activa mediante una función dedicada y la proporciona el servicio de enrutador. Aquí tenemos el archivo game-detail.component.ts listo para este tipo de enrutamiento.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 | import {Component, OnInit} from '@angular/core'; import {ActivatedRoute, Router} from '@angular/router'; import {IGame} from './game'; import {GameService} from './game.service'; @Component({ templateUrl: './game-detail.component.html', styleUrls: ['./game-detail.component.css'] }) export class GameDetailComponent implements OnInit { pageTitle = 'Game Detail'; errorMessage = ''; game: IGame | undefined; constructor(private route: ActivatedRoute, private router: Router, private gameService: GameService) { } ngOnInit() { const param = this.route.snapshot.paramMap.get('id'); if (param) { const id = +param; this.getGame(id); } } getGame(id: number) { this.gameService.getGame(id).subscribe( game => this.game = game, error => this.errorMessage = <any>error); } onBack(): void { this.router.navigate(['/games']); } } |
Ahora, en el archivo game-detail.component.html , podemos simplemente llamar onBack()
cuando el usuario hace clic para activar el código de navegación de enrutamiento.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | <div class='card' *ngIf='game'> <div class='card-header'> {{pageTitle + ': ' + game.gameName}} </div> <div class='card-body'> <div class='row'> <div class='col-md-4'> <img class='center-block img-responsive' [style.width.px]='200' [src]='game.imageUrl' [title]='game.gameName'> </div> <div class='col-md-8'> <h5 class="card-title">{{game.gameName}}</h5> <p class="card-text">{{game.description}}</p> <ul class="list-group"> <li class="list-group-item"><b>Part#:</b> {{game.gameCode | lowercase | convertToColon: '-'}}</li> <li class="list-group-item"><b>Released:</b> {{game.releaseDate}}</li> <li class="list-group-item"><b>Cost:</b> {{game.price|currency:'USD':'symbol'}}</li> <li class="list-group-item"><b>Rating:</b> <game-thumb [rating]='game.thumbRating'></game-thumb> </li> </ul> </div> </div> </div> <div class='card-footer'> <button class='btn btn-outline-secondary' (click)='onBack()'> <i class='fa fa-chevron-left'></i> Back </button> </div> </div> |
Proteja una ruta con guardias
Los diferentes usuarios de la aplicación pueden tener diferentes niveles de permisos. Quizás solo los usuarios registrados deberían poder visitar una ruta específica. En un caso como este, puede utilizar guardias en angular para proteger una ruta determinada. El enrutador angular le proporciona estas protecciones. Incluyen las siguientes opciones.
- CanActivate - navegación de guardia a una ruta
- CanDeactivate : protege la navegación de una ruta
- Resolver : recuperar datos antes de la activación de la ruta
- CanLoad : evita el enrutamiento asíncrono
Para nuestros propósitos, podemos construir una guardia que solo permitirá al usuario ver el componente de detalles del juego si se proporciona un parámetro de ruta válido en la URL. Podemos empezar con game.module.ts así.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | import {NgModule} from '@angular/core'; import {RouterModule} from '@angular/router'; import {GameListComponent} from './game-list.component'; import {GameDetailComponent} from './game-detail.component'; import {ConvertToColonPipe} from '../shared/convert-to-colon.pipe'; import {GameDetailGuard} from './game-detail.guard'; import {SharedModule} from '../shared/shared.module'; @NgModule({ imports: [ RouterModule.forChild([ {path: 'games', component: GameListComponent}, { path: 'games/:id', canActivate: [GameDetailGuard], component: GameDetailComponent }, ]), SharedModule ], declarations: [ GameListComponent, GameDetailComponent, ConvertToColonPipe ] }) export class GameModule { } |
En el fragmento de arriba, estamos diciendo que, antes de que un usuario pueda navegar al componente de detalles del juego, GameDetailGuard debe permitir el acceso. Ahora, necesitaremos crear la guardia en sí y tendrá las reglas necesarias. Haremos cumplir que la identificación no puede ser 0 o NaN. Aquí está el game-detail.guard.ts . La lógica que nos interesa está resaltada, mientras que el otro código es un modelo estándar angular.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | import {Injectable} from '@angular/core'; import {CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot, Router} from '@angular/router'; import {Observable} from 'rxjs'; @Injectable({ providedIn: 'root' }) export class GameDetailGuard implements CanActivate { constructor(private router: Router) { } canActivate( next: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean> | Promise<boolean> | boolean { const id = +next.url[1].path; if (isNaN(id) || id < 1) { alert('Invalid product Id'); this.router.navigate(['/games']); return false; } return true; } } |
Resumen del ejemplo de enrutamiento angular
Pasar parámetros
Se puede pasar cualquier número de parámetros a una ruta en angular, separados por barras. Por ejemplo, aquí hay una definición de ruta.
| {path: 'games/:id', component: GameDetailComponent} |
El parámetro se puede pasar completando la matriz de parámetros de enlace vinculada a la directiva routerLink.
| <a [routerLink]="['/games', game.gameId]"> {{game.gameName}} </a> |
En el archivo de componentes, puede leer el valor del parámetro utilizando el servicio ActivatedRoute.
| import { ActivatedRoute } from '@angular/router'; constructor(private route: ActivatedRoute) { const param = this.route.snapshot.paramMap.get('id'); } |
Ruta con código
Para activar una ruta con código, asegúrese de seguir estos pasos.
Utilice el servicio de enrutadorAsegúrese de importar el servicio y definirlo como una dependencia del constructorAgregar un método que llame al método de navegación de la instancia del servicio de enrutadorPase la matriz de parámetros de enlaceAgregar un elemento de interfaz de usuarioAprovecha el enlace de eventos para enlazar con el método creadoProteja una ruta con un guardia
Evite o permita el acceso como mejor le parezca utilizando la función de guardia proporcionada por angular. Para configurar un guardia, siga estos pasos.
Construye un servicio de guardiaImplementar el tipo de guardia (CanActivate, CanDeactivate, Resolve o CanLoad)Cree el método necesario (uno de los anteriores)Registrar el proveedor de servicios de guardia (proporcionado en)Agregue el guardia a la ruta deseadaEn este tutorial, echamos un vistazo a varias técnicas de enrutamiento disponibles en Angular. Los conceptos básicos con los que ahora deberíamos estar familiarizados son cómo pasar parámetros a una ruta en angular, cómo activar una ruta con código y cómo proteger una ruta con un guardia. El enrutamiento es un tema complicado en Angular y hay muchas más cosas que aprender, como parámetros obligatorios, opcionales y de consulta, resolución de rutas, salidas de enrutadores secundarios y carga diferida.
0 Comentarios