Usar un componente en varios módulos en Angular

Como ya debes haber averiguado si estas aquí, los componentes en tu aplicación de Angular sólo puede declararse dentro de un único módulo. Esto supone un problema debido a que en ocasiones queremos reutilizar un componente en diferentes partes de la aplicación (por ejemplo un datepicker o un Pipe).

Para resolver el problema lo más cómodo es declarar un módulo “shared” dentro del cual crearemos los componentes que queramos compartir en el resto de la aplicación.

Un ejemplo seria:

@NgModule({
  declarations: [ SharedComponent ],
  exports: [ SharedComponent ]
})
export class SharedModule {}

Después sólo necesitaremos importar el módulo allí donde queramos usarlo.

@NgModule({
   imports: [ SharedModule ]
})
export class ModuloQueUsaTuSharedComponent {}

Si tienes un grupo de Pipes que quieres usar en toda tu aplicación puede ser muy útil definir un módulo que los contenga a todos. Pero por supuesto, si piensas crear un componente para usarlo en diferentes aplicaciones debes definir un módulo que contenga tu componente de manera que puedas usarlo únicamente importándolo.

Ahora ya sabes cómo definir tus componentes dentro de un módulo como todo un profesional!

.woff2 Failed to load resource 404 NOT FOUND

Me he encontrado un error “común” en diferentes aplicaciones con Angular al colgarlas en servidores IIS. No es un fallo muy importante, pero puede sacar de quicio solucionarlo, ya que cuando debugas en local, no aparece este error.

Para solucionar este error, simplemente debes definir la extensión .woff2 con el mimeType “application/font-woff2” en el IIS.

Para hacer esto simplemente acudimos a la configuración mime del sitio web.

Alternativamente (y mi preferida) puedes establecer esta configuración en el webconfig, de esta manera no necesitarás tocar configuraciones del servidor (perfecto si no tienes acceso a la administración de IIS) y podrás mover la web de un lugar a otro con la seguridad de que funciona.

<system.webServer>
  <staticContent>
    <remove fileExtension=".woff2" />
    <mimeMap fileExtension=".woff2" mimeType="font/woff2" />
  </staticContent>
</system.webServer>

Espero que te sea útil !

Lazy Loading en Angular

¿Para qué necesito Lazy Loading?

La mayoría de veces que creamos un proyecto en Angular (o al menos en mi caso) nos interesa que la aplicación cargue de una sola vez, de manera que sacrificando un pequeño período de tiempo al inicio, conseguimos que la velocidad y la fluidez sea mucho mas alta durante el resto de la navegación.

Para aplicaciones web usadas dentro de una red local, esta opción es más que perfecta, pero ¿qué podemos hacer si nuestra aplicación crece mucho y sobrecarga el navegador? ¿y si un tipo de usuario concreto sólo debe acceder a una parte muy pequeña de la aplicación?

Este problema lo solucionamos usando Lazy Loading, que básicamente significa que la aplicación web no se cargará de una sola vez al arranque, si no que se irá cargando a demanda según la necesidad del usuario.

Empezando: las rutas

Para empezar a trabajar con Lazy Loading lo primero es entender como carga las rutas Angular.

Si ya tenemos un proyecto, vamos a abrir nuestro fichero de rutas y si no es así puedes crear un nuevo proyecto especificando  el parámetro –routing para que te genere automáticamente un fichero de rutas:

ng new --routing

En mi caso, mi fichero es el siguiente:

import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { InicioComponent } from './inicio/inicio/inicio.component';
import { GalleryShowComponent } from './gallery-show/gallery-show/gallery-show.component';
import { AboutComponent } from './about/about/about.component';
import { GallerySelectorComponent } from './gallery-selector/gallery-selector/gallery-selector.component';

const routes: Routes = [
{ path: 'informatica', loadChildren: './inicio/inicio.module#InicioModule' },
{ path: 'gallery/view', loadChildren: './gallery-show/gallery-show.module#GalleryShowModule' },
{ path: 'about', loadChildren: './about/about.module#AboutModule' },
{ path: 'gallery', loadChildren: './gallery-selector/gallery-selector.module#GallerySelectorModule' },
{ path: '**', redirectTo: '/informatica'}
];

@NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule]
})
export class AppRoutingModule { }

Aquí lo que estamos viendo es que nuestro módulo de rutas define unas rutas concretas para las diferentes páginas que tendremos, pero en lugar de referenciar una ruta a un componente, lo estamos referenciando a un módulo. Vamos a fijarnos en la galaeria (por ejemplo):

{ path: 'gallery', loadChildren: './gallery-selector/gallery-selector.module#GallerySelectorModule' },

Tenemos por un lado el path, donde especificamos la url y en loadChildren escribiremos la ruta al archivo del módulo que contiene nuestros componentes para esa página. La sintaxis especifica después de la ruta el nombre del módulo (separado por una ‘#’.

LoadChildren lo que nos indica es que el módulo tendrá sus propias rutas definidas, por lo que no las cargamos, simplemente cuando las necesitemos ya las buscará.

Fíjate que cargamos ya los componentes que forman parte de los módulos que vamos a usar.

Hasta aquí fácil no?

Creando nuestros módulos

Ahora tendremos que definir nuestros módulos y escribir en ellos las rutas que cargaremos. Para seguir con el ejemplo, os pondré el contenido del módulo gallery-selector (es el que esta funcionando en esta misma web 😀 )

import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { GallerySelectorComponent } from './gallery-selector/gallery-selector.component';
import { Routes, RouterModule } from '@angular/router';

const routes: Routes = [
  { path: '', component: GallerySelectorComponent},
];


@NgModule({
  imports: [
    CommonModule, RouterModule.forChild(routes)
  ],
  declarations: [GallerySelectorComponent]
})
export class GallerySelectorModule { }

En este fichero debemos fijarnos sobre todo en cuatro cosas:

  • Importamos los componentes que vamos a usar y forman parte del módulo
  • Definimos una variable routes con las rutas definitivas
  • Importamos Routes y RouterModule
  • Añadimos en imports “RouterModule.forChild(routes)” para que las rutas estén disponibles cuando el archivo de rutas maestro lo consulte.

 

Comandos útiles:

//Generar un módulo nuevo
ng generate module mi-modulo

//Cenerar un componente dentro de un módulo
ng generate component mi-modulo/mi-componente

Probando la configuración

Llegados a este punto nuestra aplicación ya empezará a generar el Lazy loading, pero y si queremos comprobar que realmente funciona?

Sólo tenemos que usar las herramientas de desarrollador de Chrome y fijarnos en las Request que se generan:

Como puedes ver se genera una serie de peticione sque cargan los JS inciales y sólo al visitar un álbum se genera la petición de gallery-show.module.chunk.js

Esto mismo puedes comprobarlo en el apartado de galeria de la web y verlo por ti mismo! (Recuerda que una vez compilado angular renombra los módulos con un hash!)

Como curiosidad, comentar que como convenio se suele poner un + en cada carpeta que contiene un módulo cargado por Lazy Loading. De esta manera tendremos contentos hasta a los más puritanos!