freeCodeCamp/guide/spanish/angular/routing/index.md

36 KiB

title localeTitle
Routing Enrutamiento

Enrutamiento

Motivación

El enrutamiento es esencial. Muchas aplicaciones web modernas alojan demasiada información en una página. Los usuarios tampoco deberían tener que desplazarse por el contenido de toda una aplicación. Una aplicación necesita dividirse en secciones distinguibles.

Los usuarios priorizan la información necesaria. El enrutamiento les ayuda a encontrar la sección de la aplicación con dicha información. Cualquier otra información útil para otros usuarios puede existir en una ruta completamente separada. Con el enrutamiento, ambos usuarios pueden encontrar lo que necesitan rápidamente. Los detalles irrelevantes permanecen ocultos detrás de rutas irrelevantes.

El enrutamiento es excelente para clasificar y restringir el acceso a los datos de la aplicación. Los datos confidenciales nunca deben mostrarse a usuarios no autorizados. Entre cada ruta puede intervenir la aplicación. Puede examinar la sesión de un usuario para fines de autenticación. Este examen determina lo que representa la ruta si se debe representar. El enrutamiento brinda a los desarrolladores la oportunidad perfecta de verificar a un usuario antes de continuar.

Crear una lista de rutas promueve la organización también. En términos de desarrollo, mantiene al desarrollador pensando en secciones distinguibles. Los usuarios también se benefician de esto, pero también de los desarrolladores cuando navegan por el código de la aplicación. Una lista de enrutadores programáticos pinta un modelo preciso del extremo frontal de la aplicación.

En cuanto a Angular, el enrutamiento ocupa su propia biblioteca completa dentro del marco. Todos los marcos frontales modernos soportan enrutamiento, y Angular no es diferente. El enrutamiento se realiza desde el lado del cliente mediante el hash o el enrutamiento de ubicación. Ambos estilos permiten al cliente gestionar sus propias rutas. No se necesita asistencia adicional del servidor después de la solicitud inicial.

El navegador web rara vez se actualiza utilizando el enrutamiento del lado del cliente. Las utilidades del navegador web, como los marcadores, el historial y la barra de direcciones aún funcionan a pesar de no actualizarse. Esto lo convierte en una experiencia de enrutamiento impecable que no desordena el navegador. No más recargas de páginas saltadas mientras se enruta a una página diferente.

Angular agrega una capa de abstracción sobre las tecnologías centrales utilizadas para el enrutamiento. Este artículo pretende explicar esta abstracción. Existen dos estrategias de enrutamiento en Angular: ubicación de ruta y hash. Este artículo se centra en la estrategia de ubicación de ruta, ya que es la opción predeterminada.

Además, la ubicación de la ruta puede dejar de lado el enrutamiento hash después de la versión completa de Angular Universal . En cualquier caso, las dos estrategias son muy similares en su implementación. Aprender uno aprende el otro. ¡Hora de empezar!

Configuración de RouterModule

Las utilidades de enrutamiento exportan con RouterModule disponible desde @angular/router . No forma parte de la biblioteca principal, ya que no todas las aplicaciones requieren enrutamiento. La forma más convencional de introducir enrutamiento es como su propio módulo de características .

A medida que crece la complejidad de las rutas, tenerlo como su propio módulo promoverá la simplicidad del módulo raíz. Mantenerlo estúpidamente simple sin comprometer la funcionalidad constituye un buen diseño para los módulos.

import { NgModule } from '@angular/core'; 
 import { RouterModule, Routes } from '@angular/router'; 
 
 import { AComponent } from '../../components/a/a.component'; 
 import { BComponent } from '../../components/b/b.component'; 
 
 // an array of soon-to-be routes! 
 const routes: Routes = []; 
 
 @NgModule({ 
  imports: [ RouterModule.forRoot(routes) ], 
  exports: [ RouterModule ] 
 }) 
 export class AppRoutingModule { } 

.forRoot(...) es una función de clase disponible desde la clase RouterModule. La función acepta una matriz de objetos de Route como Routes . .forRoot(...) configura rutas para la carga .forChild(...) mientras que su .forChild(...) alternativo configura para la carga perezosa.

Eager-loading significa que las rutas cargan su contenido en la aplicación desde el primer momento. La carga perezosa ocurre bajo demanda. El enfoque de este artículo es la carga impaciente. Es el enfoque predeterminado para cargar en una aplicación. La definición de la clase RouterModule se parece al siguiente bloque de código.

@NgModule({ 
  // … lots of metadata ... 
 }) 
 export class RouterModule { 
  forRoot(routes: Routes) { 
    // … configuration for eagerly loaded routes … 
  } 
 
  forChild(routes: Routes) { 
    // … configuration for lazily loaded routes … 
  } 
 } 

No se preocupe por los detalles de configuración que el ejemplo omite con los comentarios. Tener un entendimiento general servirá por ahora.

Observe cómo AppRoutingModule importa el RouterModule mientras que también lo exporta. Esto tiene sentido dado que AppRoutingModule es un módulo de características. Se importa en el módulo raíz como un módulo de características. Expone las directivas, interfaces y servicios de RouterModule al árbol de componentes raíz.

Esto explica por qué AppRoutingModule debe exportar RouterModule. Lo hace por el bien del árbol de componentes subyacente del módulo raíz. ¡Necesita acceso a esas utilidades de enrutamiento!

import { BrowserModule } from '@angular/platform-browser'; 
 import { NgModule } from '@angular/core'; 
 
 import { AppComponent } from './app.component'; 
 import { AComponent } from './components/a/a.component'; 
 import { BComponent } from './components/b/b.component'; 
 import { AppRoutingModule } from './modules/app-routing/app-routing.module'; 
 
 @NgModule({ 
  declarations: [ 
    AppComponent, 
    AComponent, 
    BComponent 
  ], 
  imports: [ 
    AppRoutingModule, // routing feature module 
    BrowserModule 
  ], 
  providers: [], 
  bootstrap: [ AppComponent ] 
 }) 
 export class AppModule { } 

El token de AppRoutingModule se importa desde la parte superior. Su token se inserta en la matriz de importaciones del módulo raíz. El árbol de componentes raíz ahora puede utilizar la biblioteca RouterModule. Eso incluye sus directivas, interfaces y servicios como ya se mencionó. ¡Muchas gracias a AppRoutingModule por exportar RouterModule!

Las utilidades de RouterModule serán útiles para los componentes de la raíz. El HTML básico para AppComponent hace uso de una directiva: router-outlet .


<!-- app.component.html --> 
 
 <ul> 
  <!-- routerLink(s) here --> 
 </ul> 
 <router-outlet></router-outlet> 
 <!-- routed content appends here (AFTER THE ELEMENT, NOT IN IT!) --> 

routerLink es una directiva de atributos de RouterModule. Se adjuntará a cada elemento de <ul></ul> una vez que se configuren las rutas. router-outlet es una directiva de componentes con un comportamiento interesante. Actúa más como un marcador para mostrar contenido enrutado. El contenido enrutado resulta de la navegación a una ruta específica. Por lo general, eso significa que un solo componente está configurado en AppRoutingModule

El contenido enrutado se representa justo después de <router-outlet></router-outlet> . Nada se rinde dentro de ella. Esto no hace una gran diferencia. Dicho esto, no espere que el router-outlet comporte como un contenedor de contenido enrutado. Es simplemente un marcador para agregar contenido enrutado al Modelo de objetos de documento (DOM).

Enrutamiento básico

La sección anterior establece la configuración básica para el enrutamiento. Antes de que ocurra el enrutamiento real, se deben abordar algunas cosas más

La primera pregunta que hay que abordar es ¿qué rutas consumirá esta aplicación? Bueno, hay dos componentes: AComponent y BComponent. Cada uno debe tener su propia ruta. Se pueden procesar desde la salida del router-outlet de AppComponent en función de la ubicación de la ruta actual.

La ubicación de la ruta (o ruta) define lo que se agrega al origen de un sitio web (por ejemplo, http: // localhost: 4200) a través de una serie de barras diagonales ( / ).

// … same imports from before … 
 
 const routes: Routes = [ 
  { 
    path: 'A', 
    component: AComponent 
  }, 
  { 
    path: 'B', 
    component: BComponent 
  } 
 ]; 
 
 @NgModule({ 
  imports: [ RouterModule.forRoot(routes) ], 
  exports: [ RouterModule ] 
 }) 
 export class AppRoutingModule { } 

http://localhost:4200/A procesa AComponent desde el router-outlet de AppComponent. http://localhost:4200/B representa BComponent. Sin embargo, necesita una forma de enrutar a estas ubicaciones sin usar la barra de direcciones. Una aplicación no debe confiar en la barra de direcciones de un navegador web para la navegación.

El CSS global (hojas de estilo en cascada) complementa el HTML debajo. El enlace del enrutador de una aplicación debe tener un aspecto agradable. Este CSS se aplica a todos los demás ejemplos también.

/* global styles.css */ 
 
 ul li { 
  cursor: pointer; 
  display: inline-block; 
  padding: 20px; 
  margin: 5px; 
  background-color: whitesmoke; 
  border-radius: 5px; 
  border: 1px solid black; 
 } 
 
 ul li:hover { 
  background-color: lightgrey; 
 } 

<!-- app.component.html --> 
 
 <ul> 
  <li routerLink="/A">Go to A!</li> 
  <li routerLink="/B">Go to B!</li> 
 </ul> 
 <router-outlet></router-outlet> 

Esta es la ruta básica! Al hacer clic en cualquiera de los elementos de routerLink, se enruta la dirección web. Lo reasigna sin actualizar el navegador web. El Router de Angular asigna la dirección enrutada a las Routes configuradas en AppRoutingModule. Coincide con la dirección de la propiedad de path de un solo objeto de Route dentro de la matriz. La primera coincidencia siempre gana, por lo que las rutas que coincidan con todas deben estar al final de la matriz de Routes .

Coincidir con todas las rutas evita que la aplicación se bloquee si no puede coincidir con la ruta actual. Esto puede suceder desde la barra de direcciones donde el usuario puede escribir cualquier ruta. Para esto, Angular proporciona un valor de ruta de comodín ** que acepta todas las rutas. Esta ruta generalmente presenta un componente PageNotFoundComponent que muestra el "Error 404: Página no encontrada".

// … PageNotFoundComponent imported along with everything else … 
 
 const routes: Routes = [ 
  { 
    path: 'A', 
    component: AComponent 
  }, 
  { 
    path: 'B', 
    component: BComponent 
  }, 
  { 
    path: '', 
    redirectTo: 'A', 
    pathMatch: 'full' 
  }, 
  { 
    path: '**', 
    component: PageNotFoundComponent 
  } 
 ]; 

El objeto Route que contiene redirectTo evita que el PageNotFoundComponent se renderice como resultado de http://localhost:4200 . Esta es la ruta de inicio de las aplicaciones. Para solucionar este problema, redirectTo la ruta principal a http://localhost:4200/A http://localhost:4200/A convierte indirectamente en la nueva ruta local de la aplicación.

pathMatch: 'full' le dice al objeto Route que coincida con la ruta local ( http://localhost:4200 ). Coincide con el camino vacío.

Estos dos nuevos objetos de Route van al final de la matriz desde que gana la primera coincidencia. El último elemento de la matriz ( path: '**' ) siempre coincide, por lo que es el último.

Hay una última cosa que vale la pena abordar antes de seguir adelante. ¿Cómo sabe el usuario dónde se encuentra en la aplicación en relación con la ruta actual? Seguro que puede haber contenido específico para la ruta, pero ¿cómo se supone que el usuario haga esa conexión? Debe haber alguna forma de resaltado aplicado a los enlaces de enrutador. De esa manera, el usuario sabrá qué ruta está activa para la página web dada.

Esta es una solución fácil. Cuando haces clic en un elemento routerLink , el Router de Angular le asigna un enfoque . Este enfoque puede desencadenar ciertos estilos que proporcionan comentarios útiles para el usuario. La directiva routerLinkActive puede rastrear este enfoque para el desarrollador.


<!-- app.component.html --> 
 
 <ul> 
  <li routerLink="/A" routerLinkActive="active">Go to A!</li> 
  <li routerLink="/B" routerLinkActive="active">Go to B!</li> 
 </ul> 
 <router-outlet></router-outlet> 

La asignación correcta de routerLinkActive representa una cadena de clases. Este ejemplo representa solo una clase ( .active ), pero puede aplicar cualquier número de clases delimitadas por espacios. Cuando el Router asigna el foco a un routerLink, las clases delimitadas por el espacio se aplican al elemento host. Cuando el enfoque se aleja, las clases se eliminan automáticamente.

/* global styles.css */ 
 
 .active { 
  background-color: lightgrey !important; 
 } 

Los usuarios ahora pueden reconocer fácilmente cómo coinciden la ruta actual y el contenido de la página. lightgrey resaltado de color gris claro se aplica al routerLink que coincide con la ruta actual. !important asegura que el resaltado sobrescriba los estilos en línea.

Rutas parametrizadas

Las rutas no tienen que estar completamente codificadas. Pueden contener variables dinámicas a las que se puede hacer referencia desde el componente correspondiente al objeto Route . Estas variables se declaran como parámetros al escribir la ruta de la ruta.

Los parámetros de ruta son opcionales u obligatorios para coincidir con una Route particular. Depende de cómo una ruta escriba sus parámetros. Existen dos estrategias: matriz y parametrización tradicional.

La parametrización tradicional comienza desde la matriz de Routes configurada en AppRoutingModule.

const routes: Routes = [ 
  // … other routes … 
  { 
    path: 'B', 
    component: BComponent 
  }, 
  { 
    path: 'B/:parameter', 
    component: BComponent 
  }, 
  // … other routes … 
 ]; 

Centrarse en las dos rutas de BComponent. La parametrización eventualmente ocurrirá en ambas rutas.

La parametrización tradicional se produce en la segunda Route BComponent. B/:parameter contiene el parameter parámetro como se indica con la : . Todo lo que sigue a los dos puntos marca el nombre del parámetro. El parameter parámetro es necesario para que la segunda Route BComponent coincida.

parameter lee el valor de lo que se pasa a la ruta. El enrutamiento a http://localhost:4200/B/randomValue asignará al parameter el valor de randomValue . Este valor puede incluir cualquier cosa además de otro / . Por ejemplo, http://localhost:4200/B/randomValue/blahBlah no activará la segunda Route BComponent. El PageNotFoundComponent en su lugar.

BComponent puede hacer referencia a parámetros de ruta de su clase de componente. Ambos enfoques para la parametrización (matriz y tradicional) arrojan los mismos resultados en BComponent. Antes de ver BComponent, examine la forma matricial de parametrización a continuación.

// app.component.ts 
 
 import { Component } from '@angular/core'; 
 import { Router } from '@angular/router'; 
 
 @Component({ 
  selector: 'app-root', 
  templateUrl: './app.component.html' 
 }) 
 export class AppComponent { 
  constructor(private router: Router) { } 
 
  routeMatrixParam(value: string) { 
    if (value) 
      this.router.navigate(['B', { parameter: value }]); // matrix parameter 
    else 
      this.router.navigate(['B']); 
  } 
 
  routeAddressParam(value: string) { 
    this.router.navigate(['B', value]); 
  } 
 } 

El sistema de inyección de dependencia de Angular proporciona una instanciación del Router . Esto permite que el componente se enrute programáticamente. La función .navigate(...) acepta una matriz de valores que se resuelve en una ruta enrutable . Algo como .navigate(['path', 'to', 'something']) resuelve en http://localhost:4200/path/to/something . .navigate(...) agrega una delimitación de ruta / marcas al normalizar la matriz en una ruta enrutable .

La segunda forma de parametrización ocurre en routeMatrixParam(...) . Vea esta línea de código: this.router.navigate(['B', { parameter: value }]) . Esta forma de parameter es un parámetro de matriz. Su valor es opcional para que la primera Route BComponent coincida ( /B ). La Route coincide independientemente de la presencia del parámetro en la ruta.

routeAddressParam(...) resuelve una ruta que coincide con el enfoque de parametrización http://localhost:4200/B/randomValue . Esta estrategia tradicional necesita un parámetro para coincidir con la segunda ruta BComponent ( B/:parameter :).

La estrategia matricial concierne a routeMatrixParam(...) . Con o sin un parámetro de matriz en su ruta, la primera ruta BComponent aún coincide. El parameter parámetro pasa a BComponent al igual que con el enfoque tradicional.

Para entender el código anterior, aquí está la plantilla HTML correspondiente.


// app.component.html 
 
 <ul> 
  <li routerLink="/A">Go to A!</li> 
  <li> 
    <input #matrixInput> 
    <button (click)="routeMatrixParam(matrixInput.value)">Matrix!</button> 
  </li> 
  <li> 
    <input #paramInput> 
    <button (click)="routeAddressParam(paramInput.value)">Param!</button> 
  </li> 
 </ul> 
 <router-outlet></router-outlet> 

En la plantilla, los valores son aceptados como entrada de texto. La entrada lo inyecta en la ruta como parámetro. Existen dos conjuntos separados de cajas para cada estrategia de parametrización (tradicional y matriz). Con todas las piezas unidas, es hora de examinar la clase de componente BComponent.

// b.component.ts 
 
 import { Component, OnInit } from '@angular/core'; 
 import { ActivatedRoute, ParamMap } from '@angular/router'; 
 
 @Component({ 
  selector: 'app-b', 
  template: ` 
  <p>Route param: {{ currParam }}</p> 
  ` 
 }) 
 export class BComponent implements OnInit { 
  currParam: string = ""; 
 
  constructor(private route: ActivatedRoute) { } 
 
  ngOnInit() { 
    this.route.params.subscribe((param: ParamMap) => { 
      this.currParam = param['parameter']; 
    }); 
  } 
 } 

BComponent resulta de cualquiera de las dos rutas BComponent en AppRoutingModule. ActivatedRoute crea una instancia de un conjunto de información útil que pertenece a la ruta actual. Es decir, la ruta que hizo que se generara BComponent. ActivatedRoute crea una instancia a través de la inyección de dependencia dirigida al constructor de la clase.

El campo .params de ActivatedRoute.params devuelve un Observable que emite los parámetros de la ruta. Observe cómo los dos enfoques de parametrización diferentes dan como resultado el parameter parámetro. El Observable devuelto lo emite como un par clave-valor dentro de un objeto ParamMap .

Entre los dos enfoques de parametrización, el parameter parámetro se resolvió de manera idéntica. El valor se emite desde ActivatedRoute.params pesar del enfoque de la parametrización.

La barra de direcciones distingue los resultados finales de cada enfoque. La parametrización de matriz (opcional para la coincidencia de Route ) produce la dirección: http://localhost:4200/B;parameter=randomValue . La parametrización tradicional (requerida para coincidencia de Route ) produce: http://localhost:4200/B/randomValue .

De cualquier manera, los mismos resultados de BComponent. La diferencia real: una Route BComponent diferente coincide. Esto depende enteramente de la estrategia de parametrización. El enfoque de matriz asegura que los parámetros son opcionales para la coincidencia de Route . El enfoque tradicional los exige.

Rutas anidadas

Routes pueden formar una jerarquía. En el DOM, esto implica que una router-outlet principal router-outlet al menos una router-outlet secundaria. En la barra de direcciones, se ve así: http://localhost/parentRoutes/childRoutes . En la configuración de Routes , la propiedad children: [] denota que un objeto Route tiene rutas anidadas (secundarias).

import { NgModule } from '@angular/core'; 
 import { RouterModule, Routes } from '@angular/router'; 
 
 import { NestComponent } from '../../components/nest/nest.component'; 
 import { AComponent } from '../../components/nest/a/a.component'; 
 import { BComponent } from '../../components/nest/b/b.component'; 
 
 const routes: Routes = [ 
  { 
    path: 'nest', 
    component: NestComponent, 
    children: [ 
      { path: 'A', component: AComponent }, 
      { path: 'B', component: BComponent } 
    ] 
  } 
 ]; 
 
 @NgModule({ 
  imports: [ RouterModule.forRoot(routes) ], 
  exports: [ RouterModule ] 
 }) 
 export class AppRoutingModule { } 
// nest.component.ts 
 
 import { Component } from '@angular/core'; 
 
 @Component({ 
  selector: 'app-nest', 
  template: ` 
  <ul> 
    <li routerLink="./A">Go to A!</li> 
    <li routerLink="./B">Go to B!</li> 
  </ul> 
  <router-outlet></router-outlet> 
  ` 
 }) 
 export class NestComponent { } 

NestComponent procesa una router-outlet después de representarse a sí misma desde otra router-outlet nivel raíz en AppComponent. El router-outlet de la plantilla de NestComponent puede representar AComponent ( /nest/A ) o BComponent ( /nest/B ).

AppRoutingModule refleja este anidamiento en el objeto Route de NestComponent. El campo children: [] contiene una matriz de objetos de Route . Estos objetos de Route también pueden anidar rutas en sus children: [] campos. Esto puede continuar por muchas capas de rutas anidadas. El ejemplo anterior muestra dos capas de anidación.

Cada routerLink contiene un ./ en comparación con / . El . asegura que el routerLink se agregue a la ruta de la ruta. El routerLink reemplaza completamente la ruta de lo contrario. Después de dirigir a /nest , . se expande en /nest .

Esto es útil para enrutar a /nest/A o /nest/B desde la ruta .nest . A y B constituyen rutas anidadas de /nest . El enrutamiento a /A o /B devuelve PageNotFound. /nest debe anteponer las dos rutas.

Eche un vistazo a AppComponent que contiene el router-outlet nivel raíz en su plantilla. AppComponent es la primera capa de anidamiento, mientras que NestComponent es el segundo.

import { Component } from '@angular/core'; 
 
 @Component({ 
  selector: 'app-root', 
  template: ` 
  <ul> 
    <li routerLink="/nest">Go to nested routes!</li> 
    <li routerLink="/">Back out of the nested routes!</li> 
  </ul> 
  <router-outlet></router-outlet> 
  ` 
 }) 
 export class AppComponent { } 

Dentro del objeto Route del nido, los children: [] contiene dos rutas más anidadas. Tienen como resultado un componente y un componente de BC cuando se enrutan desde /nest como se explicó anteriormente. Estos componentes son muy simples por el bien de la demostración. <li routerLink="/">...</li> permite navegar fuera de las rutas del nido para restablecer el ejemplo, navegando a la ruta local.

import { Component } from '@angular/core'; 
 
 @Component({ 
  selector: 'app-a', 
  template: ` 
  <p>a works!</p> 
  ` 
 }) 
 export class AComponent { } 
import { Component } from '@angular/core'; 
 
 @Component({ 
  selector: 'app-b', 
  template: ` 
  <p>b works!</p> 
  ` 
 }) 
 export class BComponent { } 

La matriz children: [] acepta el objeto Route como elementos. children: [] puede aplicar a cualquiera de estos elementos. Los hijos de estos elementos pueden seguir anidando. Este patrón puede continuar por muchas capas de anidamiento. Inserte una router-outlet en la plantilla para cada capa de enrutamiento anidado.

Las técnicas de enrutamiento se aplican independientemente del nivel de anidamiento de un objeto Route . Las técnicas de parametrización difieren en un solo aspecto. Las rutas secundarias solo pueden acceder a los parámetros de sus padres a través de ActivatedRoute.parent.params . ActivatedRoute.params apunta al mismo nivel de rutas anidadas. Esto excluye las rutas de nivel principal y sus parámetros.

Route protectores de Route son especialmente adecuados para enrutamiento anidado. Un objeto de Route puede restringir el acceso a todas sus rutas anidadas (secundarias).

Rutas vigiladas

Las aplicaciones web a menudo consisten en datos públicos y privados. Ambos tipos de datos tienden a tener sus propias páginas con rutas protegidas . Estas rutas permiten / restringen el acceso dependiendo de los privilegios del usuario. Los usuarios no autorizados pueden interactuar con una ruta protegida. La ruta debe bloquear al usuario si intenta acceder a su contenido enrutado.

Angular proporciona un conjunto de protecciones de autenticación que se pueden adjuntar a cualquier ruta. Estos métodos se activan automáticamente dependiendo de cómo interactúa el usuario con la ruta protegida.

  • canActivate(...) - se canActivate(...) cuando el usuario intenta acceder a una ruta

  • canActivateChild(...) : se canActivateChild(...) cuando el usuario intenta acceder a las rutas anidadas (secundarias) de una ruta

  • canDeactivate(...) : se canDeactivate(...) cuando el usuario intenta abandonar una ruta

Los métodos de protección de Angular están disponibles en @angular/router . Para ayudarlos a autenticarse, pueden recibir opcionalmente algunos parámetros. Tales parámetros no se inyectan vía inyección de dependencia. Bajo el capó, cada valor se pasa como un argumento al método de guardia invocado.

  • ActivatedRouteSnapshot : disponible para los tres

  • RouterStateSnapshot : disponible para los tres

  • Component - disponible para canDeactivate(...)

ActivatedRouteSnapshot proporciona acceso a los parámetros de ruta de la ruta protegida. RouterStateSnapshot expone la dirección RouterStateSnapshot la URL (localizador uniforme de recursos) que coincide con la ruta. Component referencia al componente representado por la ruta.

Para proteger una ruta, una clase que implemente los métodos de protección debe existir primero como un servicio. El servicio puede inyectarse en AppRoutingModule para proteger sus Routes . El valor de token para el servicio puede inyectarse en cualquier objeto Route .

import { NgModule } from '@angular/core'; 
 import { RouterModule, Routes } from '@angular/router'; 
 
 import { AuthService } from '../../services/auth.service'; 
 import { UserService } from '../../services/user.service'; 
 
 import { PrivateNestComponent } from '../../components/private-nest/private-nest.component'; 
 import { PrivateAComponent } from '../../components/private-nest/private-a/private-a.component'; 
 import { PrivateBComponent } from '../../components/private-nest/private-b/private-b.component'; 
 
 const routes: Routes = [ 
  { 
    path: 'private-nest', 
    component: PrivateNestComponent, 
    canActivate: [ AuthService ], // !!! 
    canActivateChild: [ AuthService ], // !!! 
    canDeactivate: [ AuthService ], // !!! 
    children: [ 
      { path: 'private-A', component: PrivateAComponent }, 
      { path: 'private-B', component: PrivateBComponent } 
    ] 
  } 
 ]; 
 
 @NgModule({ 
  imports: [ RouterModule.forRoot(routes) ], 
  exports: [ RouterModule ], 
  providers: [ 
    AuthService, 
    UserService 
  ] 
 }) 
 export class AppRoutingModule { } 

canActivate , canActivateChild y canDeactivate implement desde AuthService. La implementación del servicio se mostrará en breve junto con la implementación UserService.

UserService proporciona la información necesaria para autenticar a un usuario. Las implementaciones del método de protección AuthService realizan la autenticación. AppRoutingModule debe incluir los dos servicios en su matriz de proveedores. Esto es para que el inyector del módulo sepa cómo instanciarlos.

Las rutas anidadas existen fuera de la ruta /private-nest . El objeto Route para /private-nest contiene algunos campos más nuevos. Sus nombres deben parecer familiares ya que reflejan sus correspondientes métodos de guardia.

Cada campo dispara la implementación del método del mismo nombre dentro del servicio cuando se activa. Cualquier número de servicios puede poblar esta matriz también. La implementación del método de cada servicio se pone a prueba. Deben devolver un valor booleano o un Observable que emita un valor booleano.

Vea las implementaciones de AuthService y UserService a continuación.

// user.service.ts 
 
 import { Injectable } from '@angular/core'; 
 import { Router } from '@angular/router'; 
 
 class TheUser { 
  constructor(public isLoggedIn: boolean = false) { } 
 
  toggleLogin() { 
    this.isLoggedIn = true; 
  } 
 
  toggleLogout() { 
    this.isLoggedIn = false; 
  } 
 } 
 
 const globalUser = new TheUser(); 
 
 @Injectable({ 
  providedIn: 'root' 
 }) 
 export class UserService { 
  theUser: TheUser = globalUser; 
 
  constructor(private router: Router) { } 
 
  get isLoggedIn() { 
    return this.theUser.isLoggedIn; 
  } 
 
  login() { 
    this.theUser.toggleLogin(); 
  } 
 
  logout() { 
    this.theUser.toggleLogout(); 
    this.router.navigate(['/']); 
  } 
 } 

La misma instancia de TheUser se pasa con cada instanciación de UserService. TheUser proporciona acceso a isLoggedIn para determinar el estado de inicio de sesión del usuario. Otros dos métodos públicos permiten que UserService alterne el valor de isLoggedIn . Esto es para que el usuario pueda iniciar y cerrar sesión.

Puedes pensar en TheUser como una instancia global. UserService es una interfaz UserService que configura este global. Los cambios a TheUser desde una instanciación de UserService aplican a todas las demás instancias de UserService . UserService implementa en AuthService para proporcionar acceso a isLoggedIn de TheUser para la autenticación.

import { Component, Injectable } from '@angular/core'; 
 import { CanActivate, CanActivateChild, CanDeactivate, ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router'; 
 
 import { UserService } from './user.service'; 
 
 @Injectable({ 
  providedIn: 'root' 
 }) 
 export class AuthService implements CanActivate, CanActivateChild, CanDeactivate<Component> { 
  constructor(private user: UserService) {} 
 
  canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) { 
    if (this.user.isLoggedIn) 
      return true; 
    else 
      return false; 
  } 
 
  canActivateChild(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) { 
    return this.canActivate(route, state); 
  } 
 
  canDeactivate(component: Component, route: ActivatedRouteSnapshot, state: RouterStateSnapshot) { 
    if (!this.user.isLoggedIn || window.confirm('Leave the nest?')) 
      return true; 
    else 
      return false; 
  } 
 } 

AuthService implementa todos los métodos de guarda importados de @angular/router . Cada método de guarda se asigna a un campo correspondiente en el objeto Route del PrivateNestComponent. Una instancia de UserService crea una instancia del constructor AuthService. AuthService determina si un usuario puede continuar utilizando isLoggedIn expuesto por UserService.

Si se devuelve false de un guardia, se indica a la ruta que bloquee al usuario para evitar que enrute. Un valor de retorno de true permite al usuario proceder a su destino de ruta. Si se autentica más de un servicio, todos deben devolver true para permitir el acceso. canActivateChild guarda las rutas secundarias de PrivateNestComponent. Este método de protección cuenta para los usuarios que pasan por alto PrivateNestComponent a través de la barra de direcciones.

Los parámetros del método de guarda pasan automáticamente tras la invocación. Si bien el ejemplo no hace uso de ellos, sí proporcionan información útil de la ruta. El desarrollador puede usar esta información para ayudar a autenticar al usuario.

AppComponent también crea una instancia de UserService para uso directo en su plantilla. La instanciación de UserService de AppComponent y AuthService hace referencia a la misma clase de usuario ( TheUser ).

import { Component } from '@angular/core'; 
 
 import { UserService } from './services/user.service'; 
 
 @Component({ 
  selector: 'app-root', 
  template: ` 
  <ul> 
    <li routerLink="/private-nest">Enter the secret nest!</li> 
    <li routerLink="/">Leave the secret nest!</li> 
    <li *ngIf="user.isLoggedIn"><button (click)="user.logout()">LOGOUT</button></li> 
    <li *ngIf="!user.isLoggedIn"><button (click)="user.login()">LOGIN</button></li> 
  </ul> 
  <router-outlet></router-outlet> 
  ` 
 }) 
 export class AppComponent { 
  constructor(private user: UserService) { } 
 } 

UserService maneja toda la lógica de AppComponent. AppComponent se refiere principalmente a su plantilla. Un UserService crea una instancia como user del constructor de la clase. user datos del user determinan la funcionalidad de la plantilla.

Conclusión

El enrutamiento logra un buen equilibrio entre la organización y la restricción de las secciones de la aplicación. Una aplicación más pequeña, como un blog o una página de tributo, puede no requerir ningún enrutamiento. Incluso entonces, incluir un poco de enrutamiento hash no podría hacer daño. Un usuario solo puede querer hacer referencia a una parte de la página después de todo.

Angular aplica su propia biblioteca de enrutamiento construida sobre la API de historial de HTML5. Esta API omite el enrutamiento hash para utilizar en su lugar los pushState(...) y replaceState(...) . Cambian la URL de la dirección web sin actualizar la página. La estrategia de enrutamiento de ubicación de ruta predeterminada en Angular funciona de esta manera. La configuración de RouterModule.forRoot(routes, { useHash: true }) habilita el enrutamiento hash si se prefiere.

Este artículo se centró en la estrategia de ubicación de ruta predeterminada. Independientemente de la estrategia, muchas utilidades de enrutamiento están disponibles para enrutar una aplicación. El RouterModule expone estas utilidades a través de sus exportaciones. Rutas básicas, parametrizadas, anidadas y protegidas son posibles utilizando RouterModule. Para implementaciones más avanzadas que incluyen rutas cargadas perezosamente, vea los enlaces a continuación.

Fuentes

Recursos