Creando un componente de breadcrumbs (migas de pan) en Angular

Aprende cómo crear breadcrumbs (o migas de pan) en Angular de forma fácil para poder incluirlos en tu aplicación.

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.

¿Cuál es tu reacción?
+1
0
+1
0
+1
0
+1
1
+1
1
Total
0
Shares
Publicación anterior

Desarrollando una sencilla API REST con NodeJS y Express

Siguiente Publicación

Cómo comunicar dos componentes sin relación en Angular

Publicaciones Relacionadas