NOTA
Esta es la parte #2 de la serie sobre la creación de Minioland, una aplicación sencilla con Angular. Si te perdiste la parte #1, aquí la tienes.
Trabajando con las routes de Angular
Las routes son las herramientas que nos van a permitir navegar de una página a otra de nuestra app sin que nuestro navegador se recargue.
Por ejemplo, ahora nuestro navegador muestra http://localhost:4200/, pero implementando rutas vamos a conseguir crear direcciones tales como http://localhost:4200/home, http://localhost:4200/about, etc.
1. Creamos un archivo dentro de nuestra carpeta app, llamado app.routes.ts. El .routes no es obligatorio, pero es una convención para que identifiquemos a golpe de vista que es un archivo de rutas. Otra cosa que me ayuda a identificarlo rápidamente es su característico icono (una señal de tráfico mostrando distintas direcciones). Lo puedes conseguir a través de extensiones de vsCode, (Material icon theme).
El contenido de nuestro archivo app.routes.ts lo podemos generar con la ayuda de un Angular snippet gracias a una extensión de vsCode (busca Angular snippets en la sección de extensiones).
El resultado sería algo así:
Como podemos ver, la variable routes es un array de rutas. Cada ruta contiene un path a uno de nuestros componentes y el nombre de dicho componente.
Debemos dejar siempre al final la ruta path: '**', pues es una ruta especial que se activará si existe algún error en nuestras rutas y Angular no puede localizarlas, redireccionándonos a la ruta que le marquemos en redirectTo.
En nuestro caso, vamos a redireccionar a nuestra home.
Es necesario que importemos todos los componentes cuyas rutas vamos a necesitar.
Por tanto, añadimos nuestras rutas para los componentes de about y minions, tal y como hemos hecho con el home component.
2. Avisamos a Angular de que hemos creado un archivo de rutas.
Para ello, vamos al archivo app.module.ts (por cierto, para no ir buscando uno a uno entre nuestra lista casi infinita de archivos de la izquierda, suelo a hacer ctrl+p para buscar un archivo en vsCode), e importamos nuestro archivo de rutas ahí.
Ahora nuestro archivo app.module.ts tiene este aspecto:
3. Para que nuestras rutas funcionen tenemos que hacer un último paso, ya que Angular todavía no sabe qué ruta debe mostrar cuando nuestra app se cargue por primera vez.
Para ello, vamos al archivo que Angular utiliza para cargar nuestra app (el app.component.html) e incluimos esta etiqueta:
<router-outlet></router-outlet>
La podemos incluir dentro de un container de bootstrap, quedando nuestro código del app.component.html así:
Añadiendo routerLink y routerLinkActive
Seguimos configurando nuestras rutas.
Desde nuestro navbar vamos a gestionar hacia dónde apuntarán nuestros links. Por ahora solo tenemos el link home y uno que viene por defecto con bootstrap.
1. Eliminamos la clase active del link home y añadimos nuestros links de about y minions.
Borramos también el atributo href de nuestros links porque lo vamos a sustituir por una propiedad de Angular, el routerLink. Esta propiedad admite un array de los diferentes niveles de una ruta, donde cada nivel es un elemento del array. Por ejemplo, si queremos una ruta que nos lleve a una página (ficticia) de Sobre Nosotros y ahí dentro existiese un link a otra página llamada Aparaciones en los medios, la sintaxis sería:
[routerLink]="['/sobre-nosotros', '/medios']"
que en el navegador se traduciría en http://localhost:4200/sobre-nosotros/medios
¡Ojo!
El nombre que le demos en el routerLink debe coincidir con el que el que le hemos dado a la ruta en nuestro archivo app.routes.ts.
¡Y ya está! Ahora deberías poder navegar entre las diferentes páginas de tu app, viendo así su contenido.
2. Reforzamos la visibilidad del link de la página en la que nos encontramos en este momento.
Para ello, al parent element del elemento que tenga la propiedad [routerLink]="['/nombre de la ruta']", le añadimos routerLinkActive="active". Active es una clase CSS de bootstrap, pero podríamos añadir nuestra propia clase.
.myActiveClass.myActiveClass.myActiveClass.myActiveClass {
// tu código aquí
}
En este caso no voy a utilizar un clase CSS personalizada, sino la clase active que nos ofrece bootstrap, quedando así nuestro código del archivo navbar.component.html:
Pequeñas animaciones con CSS
En los materiales del proyecto encontrarás un archivo llamado animate. Copia su contenido y vuélcalo en el archivo styles.css, que está al nivel de la carpeta app.
El archivo es un extracto de animate.css, web muy útil.
Añadimos las siguientes clases a nuestros componentes about y home, en los divs que engloben el componente. Por ejemplo, el home component quedaría así:
Minioland
Pon un Minion en tu vida.
Diseñando el componente minions
La estructura de nuestro minions component se va a basar en los componentes cards que nos ofrece bootstrap 4.
1. Por ahora, vamos a escribir el src del elemento img en modo hardcoded, al igual que el título y el subtítulo de la card. Nos ocuparemos de hacerlo dinámico luego, utilizando una herramienta de Angular llamada service. Un service es un archivo donde guardamos datos que queremos reutilizar, con el objetivo de no repetir código.
Por ejemplo, en este caso, podríamos añadir dentro del componente minions.component.html el contenido del nombre del minion y su bio, pero esto no sería eficiente, porque en caso de necesitar esa información en otro componente deberíamos repetir el mismo código en el otro componente. Eso es lo que los services resuelven.
2. También vamos a darle un pequeño estilo a nuestra imagen. Para ello debemos modificar el archivo minions.component.css, quedando de la siguiente manera:
Nuestro archivo minions.component.html nos quedaría así:
<h2 class="mt-4">Minions <small>a mansalva</small></h2>
<hr>
<div class="container">
<div class="card-columns mb-5">
<div class="card">
<img src="../../../assets/img/tu-imagen-aqui.jpg" class="card-img-top" alt="...">
<div class="card-body">
<h5 class="card-title">Card title</h5>
<p class="card-text">This card has supporting text below as a natural lead-in to additional content.</p>
<p class="card-text"><small class="text-muted"> Last updated 3 days ago </small></p>
<button type="button" class="btn btn-outline-warning btn-block">Ver más info</button>
</div>
</div>
</div>
</div>
Como ves, hemos añadido un botón, el cual, una vez configurado, nos deberá llevar a una página donde podremos ver más información sobre el minion que hayamos hecho clic.
Introducción y creación de services en Angular
Como ya adelantamos en el anterior punto, vamos a utilizar un service para almacenar la información (aka data) de nuestros minions.
1. Creamos nuestra carpeta para almacenar nuestros servicios dentro de la carpeta app y la nombramos como queramos, aunque es una convención llamarla services.
Dentro de esta carpeta, creamos un archivo al que llamaremos minions.service.ts. El .service es una convención para poder identificarlo a golpe de vista.
Puedes utilizar un snippet de la extensión de angular para generar el esqueleto de un service.
Lo que identifica un service como tal es su decorador @Injectable().
Nuestro código del archivo minions.service.ts quedaría así:
Hemos añadido un console.log para comprobar que el service funciona.
2. Tenemos que avisar a Angular de que hemos creado un servicio nuevo.
Para ello, vamos a nuestro app.module.ts y lo declaramos ahí, tanto en los imports como en los providers, quedando nuestro archivo así:
3. Importamos el service en el componente donde queramos utilizarlo. En nuestro caso, en el minions.component.ts, quedando nuestro código de la siguiente manera:
Esta es la primera vez que vamos a usar el constructor.
4. Añadimos la palabra reservada private, ya que queremos declarar una propiedad accesible solamente en este componente.
Le damos un alias a nuestra propiedad y la declaramos como tipo MinionsService.
Al guardar, vamos a nuestra app y deberíamos ver en las dev tools de chrome nuestro console.log('minions service listo para usar, oiga!').
Ten en cuenta que ese console.log solo se ve en la página de Minions. No en el Home ni en el About, ya que sólo estamos usando el service en la página de Minions.
Esto es lo que deberías ver:

5. Rellenamos con datos nuestro MinionsService.
Para ello, vamos a nuestro service (minions.service.ts) donde declaramos una propiedad privada de tipo any[], llamada minions.
Vamos a los materiales del proyecto y abrimos el archivo .txt, donde encontramos un array de objetos. Copiamos toda la información y la pegamos dentro de nuestra propiedad minions recién creada.
6. Ya que hemos declarado nuestra data como una propiedad privada, necesitamos crear un método público para poder acceder a esa propiedad desde fuera de nuestro servicio. Este paso no es estrictamente necesario para esta simple app, pero es una buena práctica por si en el futuro queremos trabajar con APIs.
El método lo vamos llamar getMinions().
Añadimos también una interface de Angular para fortalecer nuestro código y así evitar errores. Básicamente, con la interface lo que hacemos es declarar que cada minion va a tener un name, bio, img, birth y side, de manera que si de ahora en adelante nos equivocamos en alguna de esas características, Angular nos lanzará un error diciendo que cada minion puede tener esas únicas características, ni más ni menos.
El código de nuestro MinionsService quedaría de la siguiente manera:
7. Y ahora, ¿cómo trasladamos los datos que hemos puesto en nuestro MinionsService para que se vean reflejados en nuestro MinionsComponent? Declarando una propiedad, a la que llamaremos también minions, que será de tipo Minion (esto sale de nuestra interface) en nuestro minions.component.ts y asignándole el valor de la propiedad minions de nuestro archivo minions.service.ts y el método para obtener la información de los minions (getMinions() )
Esto lo vamos a hacer dentro del bloque del ngOnInit() { }.
Y así de sencillo es conectar un servicio con un componente.
Haz un console.log de minions en el ngOnInit() y verás que la lista de minions aparece en tus dev tools.
Aquí el código de minions.component.ts tal y como quedaría:
Aplicando el *ngFor para generar minions
Vamos a usar una herramienta verdaderamente útil que nos proporciona Angular: el *ngFor. El *ngFor es una directive de Angular que nos permite realizar loops de manera muy sencilla.
La vamos a usar para generar minions dinámicamente a partir del esqueleto o plantilla base que tenemos definida en nuestra card de bootstrap del minions.component.html: su foto, su nombre, un resumen de su biografía y un botón para ver su información completa.
En nuestro minions.component.ts tenemos nuestra variable local minions. Esa variable la vamos a usar de referencia en el archivo minions.component.html.
1. La forma de usar este loop es creando una variable local, a la que, siendo lógicos, llamaremos minion y le pasaremos nuestra variable minions del archivo minions.component.ts.
Comprueba por ti mismo la conexión que acabamos de crear poniendo el ratón encima de minions y clicando ctrl+clic izq. Verás que te lleva a tu archivo de typescript (minions.component.ts).
2. Convertimos las propiedades de nuestros minions en dinámicas, y no hardcoded, como estaban hasta ahora.
Para ello vamos a usar una técnica de Angular llamada property binding, la cual nos permite convertir atributos de un elemento HTML, como el src, en atributos que interactúan con Angular informándole de lo que nosotros le digamos.
En este caso, vinculamos el atributo src del elemento img y utilizamos nuestra variable local minion del *ngFor junto con nuestra propiedad img, creada para cada minion en nuestra interface.
Hacemos lo mismo con el atributo alt de la imagen.
Con el nombre y la bio del minion no podemos hacer property binding, así que utilizamos otra técnica para informar a Angular llamada string interpolation.
Nuestro código del minions.component.html quedaría así:
<h2 class="mt-4">Minions <small>a mansalva</small></h2>
<hr>
<div class="container">
<div class="card-columns mb-5">
<div class="card" *ngFor="let minion of minions">
<img [src]="minion.img" class="card-img-top" [alt]="minion.name">
<div class="card-body">
<h5 class="card-title"> {{ minion.name }} </h5>
<p class="card-text"> {{ minion.bio }} </p>
<p class="card-text"><small class="text-muted"> {{ minion.birth }} </small></p>
<button type="button" class="btn btn-outline-warning btn-block">Ver más info</button>
</div>
</div>
</div>
</div>
Y ahora nuestra página de Minions se ve así:

Configurando la navegación entre nuestras páginas
Ya tenemos el componente MinionsComponent, que nos muestra una visión general de todos nuestros minions. Ahora es momento de crear otro componente que será la página donde seremos dirigidos cuando hagamos click en el botón de Ver más info de alguna de las cards de nuestro componente MinionsComponent.
1. Creamos el componente (al que llamaremos minion) en app --> components, usando Angular CLI.
ng g c minion --skipTests
2. Actualizamos nuestro archivo de rutas (app.routes.ts), informando así a Angular de que hemos creado una nueva route para poder navegar hasta y desde ella. Así nos queda:
Aquí viene lo nuevo. Cuando le pasemos un path a la ruta, no podemos simplemente llamarlo minion, sino que necesitaremos pasarle un parámetro. Sencillamente porque cuando hagamos clic en el botón de Ver más info de nuestro minion Dave, por ejemplo, Angular nos llevará al componente que acabamos de crear (MinionComponent) pero debemos informarle cuál de todos nuestros minions queremos ver, que deberá coincidir, claro, con el minion que hayamos hecho clic. En este caso, deberá mostrarnos la "página de Dave", por así decirlo.
Es el mismo concepto que cuando estás navegando por Netflix y seleccionas una serie. Al seleccionarla, Netflix te lleva a la página de esa serie.
Pues lo mismo aquí, mil veces más sencillo
A nuestro parámetro lo vamos a llamar id porque ése es el standard y lo lógico: cada uno de nuestros minions es único y por tanto, tiene sentido que cada uno tenga un id distinto.
¿Pero de dónde sacamos ese id, si no le hemos dado ninguno a nuestros minions?
Pues del array de minions que tenemos en nuestras dev tools de chrome, que sale de nuestro console.log del minions.component.ts:

Así que con esta información del console.log podemos saber qué índice (o id) tiene cada uno de nuestros minions. Por ejemplo, Kevin tiene el índice 0.
3. A este índice podemos acceder fácilmente desde nuestro código en minions.component.html usando la propiedad index y añadiéndola como parte del *ngFor:
<div class="card" *ngFor="let minion of minions; let i = index;">
4. Nos creamos el método seeMinion() en el archivo de typescript de ese mismo componente (en seguida lo explicaremos) :
seeMinion(idx: number) {
this.router.navigate(['/minion', idx]);
}
Nuestro código del minions.component.html quedaría de la siguiente manera:
<h2 class="mt-4">Minions <small>a mansalva</small></h2>
<hr>
<div class="container">
<div class="card-columns mb-5">
<div class="card" *ngFor="let minion of minions; let i = index;">
<img [src]="minion.img" class="card-img-top" [alt]="minion.name">
<div class="card-body">
<h5 class="card-title"> {{ minion.name }} </h5>
<p class="card-text"> {{ minion.bio }} </p>
<p class="card-text"><small class="text-muted"> {{ minion.birth }} </small></p>
<button type="button" class="btn btn-outline-warning btn-block" (click)="seeMinion(i)">Ver más info</button>
</div>
</div>
</div>
</div>
Para que nuestro método funcione tenemos que importar el módulo Router de @angular/router en minions.component.ts e inyectarlo en nuestro constructor creando una variable de ese tipo (Router).
Nuestro código del minions.component.ts nos quedaría así:
¡Y voilà! Ahora si clicas en cualquier minion desde la página de minions te llevará a su página específica, que de momento debería decir no más que minion works!
Por ejemplo, si clicas en el botón de Ver más info del minion Kevin, verás que tu url ahora es http://localhost:4200/minion/0 , siendo 0 el id que representa a nuestro querido Kevin.
THE END! (POR AHORA...)
Y hasta aquí la parte #2 de esta serie de posts sobre cómo construir una sencilla aplicación con Angular. Si quieres seguir aprendiendo, aquí tienes la parte #3. Y si te perdiste la parte #1, aquí la tienes.
Más recursos de aprendizaje
En mi experiencia, la manera más eficaz para aprender Angular es combinando varias vías de aprendizaje. Uno de mis métodos favoritos son los vídeo-cursos y mi plataforma predilecta para eso es Udemy. He hecho varios cursos pero sólo recomiendo aquellos que verdaderamente me han sido útiles. Aquí van:
Si necesitas apoyo en forma de libro, puede que éstos te sirvan de ayuda:
La programación es un mundo que evoluciona a una velocidad de vértigo. Los autores de estos libros lo saben, por eso suelen encargarse de actualizar su contenido regularmente. Asegúrate de que así sea antes de adquirirlos
Participo en el programa de afiliados de Udemy y Amazon, lo que significa que, si compras alguno de estos cursos y/o libros, yo me llevaré una pequeña comisión y a ti no costará nada extra. Vamos, lo que se dice un win-win
COMPARTIR ES VIVIR
Si crees que este post puede serle útil a alguien, ¡compártelo!