Rutas
Rutas
Las rutas sirven para dividir y abstraer mejor por rutas de dominio la aplicación.
Al generar la app puedes elegir que se agreguen los archivos para el routing de una vez, y queda de la siguente manera:
Observa la primera regla que tiene el path
vacío y un redirectTo
para redireccionar al componente home cuando no se ingresa ninguna ruta. Utiliza también la opción pathMatch
para asegurar que la ruta sea exacta. Podría quedar solamente path: '',
y sin otra regla con redirect hacía home y funcionaria igual.
También para las páginas no encontradas se usa path: '**'
.Es muy importante que esta regla para manejo de rutas no definidas se encuentre ubicado en el último lugar del array. Angular analiza las rutas en el mismo orden en que las defines. Si esta regla se encuentra en primer lugar, puede anular las demás y darte algunos problemas.
Finalmente, tienes que importar la directiva <router-outlet>
en el componente raíz de tu aplicación.
Redirecciones sin recargar routerLink
routerLink
Para poder movernos entre rutas sin recargar nuestra página debemos agregar a nuestras anclas ‘<a>
’ la directiva routerLink
envés del atributo href
para que Angular determine que no haga una recarga de la página.
Ancla activa routerLinkActive
routerLinkActive
Puedes definir una clase para cuando una ruta coincida con el routerLink al agregar la directiva routerLinkActive.
RouterLink con parametros
Podemos hacer uso de un arreglo y encerrar routerLink como si fuera un property binding
O también utilizar el string interpolation
Capturando parámetros de URL
Los parámetros se crean así en el archivo routing:
Observa que ambas rutas apuntan al mismo componente, eso está bien. La diferencia estará en que la segunda ruta posee :productId
y podrás capturar el parámetro utilizando ese mismo nombre.
Inyección de servicios necesarios
En el componente correspondiente, inyecta el servicio ActivatedRoute y también importa Params para tipar tus datos y manipularlos más fácilmente. Ambos imports provenientes de @angular/router
.
Captura de parámetros síncronos
El mejor lugar para capturar parámetros de URL, sean síncronos o no, es utilizando los hooks de ciclo de vida de Angular, más concretamente ngOnInit()
.
this.route.snapshot.params
: Esta propiedad es una instantánea (snapshot) de los parámetros de la ruta en un momento determinado. Esto significa que si se accede a esta propiedad en un momento en particular, se obtendrán los valores de los parámetros de la ruta en ese momento. Si los parámetros cambian posteriormente, esta propiedad no se actualizará.
snapshot.paramMap
también proporciona algunas funciones útiles para trabajar con los parámetros de la ruta, como por ejemplo:
get()
: Devuelve el valor de un parámetro especificado por su clave.getAll()
: Devuelve todos los valores de un parámetro especificado por su clave.has()
: Comprueba si un parámetro está presente en la ruta.keys()
: Devuelve una lista de todas las claves de los parámetros de la ruta.
Por otro lado, snapshot.params
simplemente proporciona un objeto con pares clave-valor y no tiene funciones adicionales para trabajar con los parámetros.
Captura de parámetros asíncronos
Una URL puede cambiar y a veces es conveniente estar escuchando de forma activa los cambios en la misma. Para que los Observables nos ayuden a estar atentos a estos cambios, Angular también nos permite suscribirnos a los cambios en los parámetros de URL de la siguiente manera.
Query Params
Los parámetros de ruta, por ejemplo /catalogo/:categoryId
, son obligatorios. Sin el ID la ruta no funcionaría. Por otro lado, existen los parámetros de consulta que los reconocerás seguidos de un ?
y separados por un &
, por ejemplo /catalogo?limit=10&offset=0
.
1. Creando rutas con parámetros
La directiva queryParams recibe un objeto y creará la ruta /catalogo?category=electronica
2. Capturar parámetros en las rutas
Para capturar estos datos en el componente, es aconsejable realizarlo en el hook de ciclo de vida ngOnInit()
.
Suscribiéndote a queryParams, podrás capturar y hacer uso de esta información.
Con un punto se puede hacer refencia a la ruta actual.
Optimización de tiempo y recursos
Para reducir el peso de las aplicaciones, aparece el concepto de Lazy Loading y el CodeSplitting que plantean la división del código fuente Javascript en pequeños módulos y solo cargar aquellos que el usuario necesite, cuando realmente los necesite.
Code Splitting
Es la técnica que permite dividir el código y sus recursos en varias partes (chunks) que se pueden descargar en demanda y no solo en un solo archivo como se hace tradicionalmente, mejorando la velocidad de carga de las páginas.
Lazy loading
Para poder implementar lazy loading es necesario modularizar nuestra aplicación.
Modularizacion
Un módulo encapsula varios elementos de una aplicación. Por lo general se modulariza cada grupo de componentes u otros elementos relacionados de nuestra aplicación.
Angular logra esto utilizando con @NgModule
, cada módulo es como una isla que agrupa como se mencionó antes ciertas características en específico.
En otras palabras, no se pueden tener módulos que sirvan para toda la aplicación, cada vez que quieras trabajar con algún componente o característica en algún modulo en particular deberás importarlo, un ejemplo sería cuando usamos el módulo de ReactiveFormsModule.
Cuáles son los módulos en Angular
Por defecto, Angular posee un solo módulo en el archivo app.module.ts
. Todos tus componentes, servicios, pipe, etc. se importan aquí. Utiliza un decorador llamado @ngModule()
con un aspecto similar al siguiente:
Un módulo en Angular se define utilizando el decorador @NgModule
, que recibe un objeto de metadatos con las siguientes propiedades principales:
Declarations: Esta propiedad contiene un arreglo de componentes, directivas y pipes que pertenecen a este módulo.
Imports: Aquí se especifican otros módulos cuyos componentes, directivas y pipes son necesarios en este módulo.
Exports: Define los componentes, directivas y pipes que pueden ser utilizados por otros módulos que importen este módulo.
Providers: Proporciona los servicios que deben ser disponibles de forma global o para el ámbito de este módulo.
Bootstrap: Especifica el componente raíz que Angular debe iniciar para arrancar la aplicación (principalmente en el módulo raíz
AppModule
).
Al modularizar una aplicación, cada módulo tendrá sus componentes exclusivos, servicios o los archivos que fuesen a necesitar.
Tipos de Módulos en Angular
Podemos identificar varios tipos de módulos.
Root Module: Módulo por defecto de Angular que inicia toda la aplicación (App Module).
Core Module: Son servicios que pueden ser usados en diferentes módulos y componentes, recordar que los servicios se inyectan con
provideIn : ‘root’
y se puede usar en cualquier parte de la aplicación (Globales).Routing Module: Son módulos especiales para la definición de rutas.
Feature Domain Module: Son los módulos propios de tu aplicación.
Shared Module: Posee servicios o componentes compartidos por toda la aplicación, Por ejemplo, componentes pipes y directivas que se quieran usar en toda la aplicación.
Vistas Anidadas
Trabajar con Angular en una aplicación modularizada da la posibilidad de que, cada módulo, tenga a su vez N cantidad de páginas hijas.
La técnica de vistas anidadas, nos sirve para incluir ciertos componentes a otros componentes que se especifiquen, un ejemplo sería que solo las rutas que utilicen el componente Layout tendrian incorporado el componente Header
Veámos un ejemplo.
1. Crear módulos necesarios
Podríamos decir que cada grupo de rutas podría ser un módulo, entonces creamos el modulo de products.
Suponiendo que tenemos una estructura como esta:
Crearemos un modulo para la seccion de website
ng generate module pages/website --routing
2. Preparación del routing
A continuación, prepara tu app-routing.module.ts
para Lazy Loading y CodeSplitting importando los módulos de la siguiente manera:
Observa que, a excepción del componente NotFound, solo estamos importando los módulos de una manera especial. Con loadChildren
, cada módulo será enviado bajo demanda, ya que ahora se cargan de manera asíncrona.
3. Renderizar módulos
El componente principal de tu aplicación será el encargado de renderizar cada módulo. Para esto, asegúrate de que solo posea el <router-outlet>
, porque es todo lo que necesitas para lograrlo.
Incluso puedes borrar el archivo app.components.html y colocar el <router-outlet>
dentro de la propiedad template
en el decorador @Component()
para simplificar tu código.
4. Componentes base
Creamos un componente Layout
ng g c components/layout
En el layout del módulo website, tiene su propio <router-outlet>
además del componente para la barra de navegación.
Este componente de barra de navegacion no va a aparecer en otras rutas si no se especifica, por ejempo en la ruta con el componente de NotFound no deberá aparecer.
5. Routing de cada módulo
Finalmente, cada <router-outlet>
de cada módulo renderizará los componentes que posea dichos módulos. Para esto, prepara el routing de cada módulo de la siguiente manera.
Presta atención a la propiedad children
que construye las nuevas reglas para las rutas.
De esta manera, puedes tener un <router-outlet>
dentro de otro <router-outlet>
para renderizar páginas hijas de cada módulo y tener un layout personalizado por nada uno de ellos. Además de estar optimizado el rendimiento de tu aplicación gracias al Lazy Loading y CodeSplitting.
Podemos ver que la correr nuestra aplicación se genera aparte un archivo para el modulo de website
SharedModule
Un módulo compartido donde guardarás los componentes, pipes, directivas o servicios que dos o más de tus otros módulos necesitarán y los puedes incluir en tus otros modulos.
Nota: Un componente solo puede pertenecer a un módulo, no a dos.
All Modules y Custom Strategy
Al haber activado la técnica de Lazy Loading, puedes personalizar el envío de estos módulos al cliente con diferentes estrategias.
Precarga de módulos bajo demanda
Por defecto, la aplicación enviará al cliente solo el módulo que necesita. Si ingresas al módulo website, solo se cargará su respectivo archivo JS.
Si el usuario solicita otro módulo, este se cargará solo cuando sea necesario.
Esto puede causarte problemas, ya que si el módulo solicitado es algo pesado o la conexión es lenta, tardará varios segundos en estar listo y no será buena la experiencia de usuario.
Precarga de todos los módulos
Puedes decirle a tu aplicación que, por defecto, precargue todos los módulos con la siguiente configuración:
Importando PreloadAllModules desde @angular/router
, lo pasas como parámetro al import
en el decorador @NgModule()
. El browser va a descargar primero los archivos base, es decir, los que tienen que ver con la carga inicial y luego que este libre el browser comenzará a precargar los módulos restantes.
Es ideal para aplicaciones que no sean muy grandes o no ocupen muchos módulos.
En el caso de tener muchos módulos es mejor usar una estrategia personalizada para evitar sobrecargar el hilo principal. De esta forma se establece que módulos se van a precargar.
Estrategia personalizada de precarga
Precargar todos los módulos a la vez, puede ser contra producente. Imagina que tu aplicación posea 50 o 100 módulos. Sería lo mismo que tener todo en un mismo archivo main.js
.
Para solucionar esto, puedes personalizar la estrategia de descarga de módulos indicando qué módulos si se deben precargar y cuáles no.
1. Agrega metadata a cada ruta
Agrégale a cada regla en el routing de tu aplicación, metadata para indicarle a cada módulo si debe ser precargado, o no.
Con la propiedad data: { preload: true }
, le indicas al servicio CustomPreloadingStrategy si el módulo debe ser precargado cuando el browser halla descargado ya todos los archivos base.
2. Crea un servicio con estrategia personalizada
Crea un servicio al cual llamaremos CustomPreloadingStrategy con la siguiente lógica.
El servicio implementa PreloadingStrategy y sobreescribiendo el método preload()
, hace uso de la metadata para desarrollar tu propia lógica de renderizado de módulos.
3. Importa tu estrategia
Finalmente, importa tu estrategia personalizada en el routing.
De esta manera, ya puedes personalizar qué módulos serán enviados al cliente y cuáles no, mejorando así el rendimiento de tu aplicación.
Guardianes
Hay veces que queremos que determinadas áreas de nuestra aplicación web estén protegidas y solo puedan ser accedidas si el usuario ésta logueado por ejemplo, o incluso que solo puedan ser accedidas por determinados tipos de usuarios.
CanLoad: Sirve para evitar que la aplicación cargue los módulos Lazy si el usuario no está autorizado a hacerlo.
CanDeactivate: Mira si el usuario puede salir de una página, es decir, podemos hacer que aparezca un mensaje, por ejemplo, de confirmación, si el usuario tiene cambios sin guardar.
CanActivateChild: Mira si el usuario puede acceder a las páginas hijas de una determinada ruta.
CanActivate: Mira si el usuario puede acceder a una página determinada.
1. Creando el primer guard
Al utilizar este comando, nos hará una pregunta sobre qué interfaz quieres que implemente por defecto:
Al auto generar el código, verás tu primer Guard con el siguiente aspecto.
Un Guard puede devolver un booleano, una promesa con un booleano o un observable, también con un booleano. Dependiendo la lógica que tengas que aplicar para el caso sea síncrona o asíncrona.
2. Importando el guard
Ahora importa el nuevo Guard el routing de tu aplicación.
Agrégale a las rutas que quieras segurizar canActivate: [ AdminGuard ]
.
De esta manera, ya puedes implementar la lógica que necesites para cada Guard. En este caso, permitir el acceso al módulo CMS, por ejemplo, solo para usuarios administradores.
Last updated