En este tutorial vamos a ver cómo hacer un componente para breadcrumbs que sea reutilizable en nuestros otros componentes y así no requerir poner a mano breadcrumbs por cada componente y evitar duplicado de código.
Lo primero es, ¿qué es un breadcrumb o una miga de pan en esta área?, bueno, básicamente es una “lista” que le permite a los usuarios saber su ubicación dentro de la página y poder moverse “hacia atrás” en el hilo del sitio.
Me explico un poco mejor con un ejemplo, pensemos que estamos en una tienda en línea, por lo que tenemos nuestra página de inicio, de ahí queremos comprar por ejemplo unas bocinas, entonces, navegamos a la categoría de Accesorios para computadoras, después, vemos que están varias sub-categorías y entre estas se encuentran las bocinas, entramos a dicha categoría y luego vemos la marca Bose, de ahí nos agradan unas bocinas y entramos a estas.
Ahora que ya navegamos “hasta el fondo” donde se encontraba el producto que nos agradó podemos ver que en caso de existir una breadcrumb en esta tienda en línea, sería entonces:
Inicio > Accesorios para computadoras > Bocinas > Bose > Bocina bose modelo 123 negra.
Pero pensemos que queremos de otra marca porque están muy caras, entonces para no tener que navegar desde el inicio hasta la sub-categoría de bocinas, simplemente hacemos clic en “Bocinas” y nos dirige a esa sección.
Estas son las migas de pan y son muy útiles para UX.
Entonces, teniendo en cuenta qué es lo primero será crear un componente en nuestra aplicación en Angular, sin HTML externo ni CSS, todo lo haremos dentro del mismo componente.
Una vez que tengamos el componente creado, requerimos de importar dos librerías que nos serán útiles, @angular/core
y @angular/router
de estas dos librerías, vamos a importar, Component, Input y OnInit en core y RouterModule en router.
Ahora, ¿es necesario OnInit?, como sabremos OnInit es la función que se llama cuando el componente se inicia, pero en este caso también puede estar el código que implementaremos en el constructor sin problema.
Ya que tengamos lo anterior, en nuestro @Component({})
hay que incluir el selector y el código de la plantilla, sin embargo por ahroa la plantilla la dejaremos limpia pero el selector hay que asignarle el nombre app-breadcrumb-component
y recordemos que el estilo lo pondremos dentro, así que también lo dejaremos limpio pero preparado.
Ya dentro de la clase del componente, hay que crear 3 variables. La primera se llamará parts
y será de tipo @Input, la segunda se llamará activePage
y también será de tipo @Input y la tercerá será un array llamado url
Lo primero será entonces entender un poco de la lógica del funcionamiento:
Cuando recibimos un parámetro a través del componente, lo veríamos de la siguiente forma:
<app-breadcrumb-component [activePage]="'Bocinas'" [parts]="['Inicio', 'Accesorios para Computadora', 'Bocinas']"></app-breadcrumb-component>
Y de esta forma nos indicará entonces que la página activa es la de Bocinas, de esta manera pueden aplicar una clase específica a la página activa, o como en mi caso, ponerla al mero inicio como indicador.
Posterior vemos las partes del breadcrumb que pasamos, Inicio, Accesorios para Computadora y Bocinas, esto puede ser un array que se generaría dinámicamente, es decir, jalarlo desde un API y pasarlo al componente.
Ahora bien ya que tenemos esta parte implementada, veamos la lógica de nuestro componente:
this.parts.unshift('Inicio');
const limit = this.parts.length;
for (let i = 0; i < limit; i++) {
if (i > 0) {
this.parts.splice(i * 2, 0, '-');
}
}
this.parts.pop();
for (let i = 0; i < limit; i++) {
if (this.parts[i] !== 'inicio' && this.parts[i] !== '-') {
this.url[i] = (typeof this.url[i - 2] === 'undefined' ?
'' : this.url[i - 2]).replace(/\s/g, '-') + '/' + this.parts[i].replace(/\s/g, '-');
}
}
Vamos línea a línea, lo primero es que sabemos que “Inicio” siempre deberá existir, por lo que en este caso nos lo podemos ahorrar en la sección anterior, y no incluirlo en ...[parts]="['Inicio...
. Lo segundo es saber el límite de partes que hay, esto lo asignamos en una constante para evitar ponerlo en cada for por separado, aunque no es necesario que sea así.
Luego, nuestro primer ciclo lo que hará es que si hay al menos 1 parte, es decir algo como ['Accesorios']
entonces vamos a estar agregando los guiones como separadores como parte del array, es decir, si tenemos:
['Accesorios', 'Bocinas']
convertiriamos este array en el siguiente:
['Accesorios', '-', 'Bocinas', '-']
Ahora, en la siguiente línea, retira el último '-'
del array, esto se puede evitar agregando que, si estamos en limit — 1
no haga nada el ciclo pero por ahora lo pondremos así.
Posterior lo que hará el segundo ciclo será convertir las partes en rutas, es decir, Accesorios
pasaría a ser parte de las URLs, como Inicio/Accesorios/Bocinas
para poder generar enlaces, excepto, en la parte final.
Para este punto, recomiendo que, siempre las [parts]
de nuestro componente sean en minúsculas y usar la propiedad de CSS text-transform
para capitalizar las letras. De otra forma hay que agregar .toLowerCase()
en la primer sección de la primer condicional del if
.
Después, en este if, revisa que no sea un -
la parte del array que estamos pasando.
Si se cumplen estas dos condiciones, entonces generamos en nuestro array de URLs una parte, ahora bien, ¿qué pasa con las secciones como: accesorios para computadora
donde hay espacios?, bueno para eso está el operador ternario. Este revisará que aún haya partes disponibles y que si las hay, se les reemplace el espacio por un guión, entonces en el caso anterior pasaría a ser: accesorios-para-computadora
.
Ya que tenemos esto, vamos a la lógica de HTML.
<div class="breadcrumb">
<h4>{{ activePage }}</h4>
<ul>
<li *ngFor="let items of parts; let i = index" [ngClass]="{ 'inicio': i === 0, 'separator': i % 2 === 0 && i > 0 }">
<div *ngIf="i % 2 !== 0 && i > 0">
<a routerLink='{{ url[i] }}'>
{{ "Breadcrumbs." + items | translate }}
</a>
</div>
<div *ngIf="i % 2 === 0 && i > 0">-</div>
</li>
</ul>
</div>
En este caso, veremos la primer sección que es hacer un wrapper del breadcrumb, es decir, empaquetar todo en un div, ¿por qué?, para que sea más sencillo estilizar con SCSS por ejemplo bajo .breadcrumb
.
Ahora bien, el <h4>
será como nuestro indicador de página activa
. Esto no es totalmente necesario pero como dije, pueden también usarlo como parte de las clases para indicar el activo.
Después, tenemos un ciclo para repetir nuestra lista, con este, vamos a pasar por cada elemento de las partes y asignaremos un índice que nos servirá para el array de las rutas.
Adicionalmente, agregaremos una clase para cuando estemos en el inicio, como para poner un ícono si es que lo requerimos, si no, pues es retirable, así como una clase para que nos “empuje” los elementos cuando se cumple la condicional de que sea un par (osea un guión) y que sea mayor que 0 el índice, es decir, estemos en el primer breadcrumb real, no en inicio.
En otras palabras del siguiente:
Inicio — Accesorios
Formamos que, Inicio
pueda tener la clase inicio
que le agregue un ícono, y que Accesorios
tenga la clase separador. Esto lo hago yo de forma interna para darle mejores estilos a cada elemento pero se puede retirar.
Luego, si estamos en una ruta, entonces, agregamos el enlace, por último, recuperamos nuestro -
en caso de que estemos en una parte.
Y eso es todo, lo último es estilizar al gusto. En mi caso, así luce el final:
De tarea les dejo actualizar mi código para simplificarlo y retirar partes innecesarias que puedan ver, como que no es necesario el doble ciclo en la lógica, o que la clase
de inicio
tampoco se vean necesarias y reutilizar el de parts
para incluir los -
en vez de la condicional en el div.
Nos vemos en la siguiente publicación.