Recipes App – Parte 3

Retomamos este proyecto con Angular, donde estamos construyendo una recipes app, con la que podrás buscar recetas y tener un sistema que te permita apuntar tu "lista de la compra". Si acabas de aterrizar aquí, puedes ver las partes #1 y #2 antes 🚀.

En esta tercera parte vamos a crear nuestra propia directiva (en inglés, custom directive) para abrir y cerrar el menú desplegable del header y de una receta individual. Es decir, los botones Manage Manage recipe.

menú desplegable = dropdown

👉 Si no sabes cómo crear una custom directive, aquí tienes una guía paso a paso donde aprenderás a hacerlo.

Cómo crear una custom directive para crear menús tipo dropdowns

Recordemos que los dropdowns no funcionan porque en su día únicamente importamos el archivo CSS de Bootstrap, pero no sus archivos de JavaScript (en adelante, JS). Así lo hicimos porque es una buena práctica usar únicamente Angular para interactuar con nuestro DOM.

1. Creamos manualmente la directiva en la carpeta shared. Llamamos al archivo dropdown.directive.ts.

2. Usamos el decorador @Directive para especificar que estamos creando una directiva.

3. Dentro del decorador, creamos el selector para definir de qué forma usaremos esta directiva, que en este caso tomará la forma de un atributo HTML, así que la escribimos entre corchetes. La llamamos appDropdown.

💁‍♀️ La funcionalidad que queremos configurar consiste en añadir/quitar dinámicamente la clase de CSS llamada show,  que es una clase que viene por defecto con Bootstrap. Digo "dinámicamente", porque queremos alternar la inclusión de esa clase dependiendo de si ya está incluida cuando hagamos clic en un botón o no. Para eso, deberemos crear un código que haga de event listener, "escuchando" cuando el usuario haga clic en alguno de los botones. 

alternar = toggle

import { Directive } from '@angular/core';

@Directive({
    selector: '[appDropdown]'
})
export class DropdownDirective {

}

4. Declaramos la directiva en el app.module.ts.

import { DropdownDirective } from './shared/dropdown.directive';

@NgModule({
  declarations: [
 ...
    DropdownDirective

Para entender lo que hace la clase de CSS  de la que estamos hablando (show), vamos a probarla en el botón del header, sobre el <div> que contiene la clase dropdown-menu, en el archivo header.component.html. La añadimos en ese <div>.

        <div class="dropdown-menu show">

Si guardas, podrás ver el menú desplegado de la siguiente manera:

¡Perfecto! Esto demuestra la que la clase show funciona. Pero el pequeño menú se nos sale de la pantalla, así que le añadimos la clase dropdown-menu-right.

menu con clase show
        <div class="dropdown-menu show dropdown-menu-right">

Esto debería mostrar el menú dentro de la página, y no cortado, como antes.

5. Ya podemos quitar la clase show y dejar el <div> como estaba, porque sólo lo hemos hecho para ver cuál era el efecto al añadirla.

6. Añadimos la directiva en el botón Manage del header.

        <a class="nav-link dropdown-toggle" href="#" role="button" data-toggle="dropdown" appDropdown>
            Manage
        </a>

7. En la directiva, creamos una propiedad de tipo boolean llamada isOpen, en referencia al estado inicial en que queremos que esté nuestro dropdown. Como queremos que esté cerrado, isOpen debería ser false. 

8. En el constructor inyectamos dos propiedades de tipo ElementRef y Renderer2. Recuerda que podemos transformarlas en propiedades de TS añadiéndoles la keyword "private" delante. Las podemos llamar eleRef renderer, respectivamente. 

El ElementRef nos da acceso a un elemento HTML

El Renderer2 nos da acceso al DOM

@Directive({
  selector: '[appDropdown]'
})
export class DropdownDirective {
  isOpen = false;
  constructor(private eleRef: ElementRef,
              private renderer: Renderer2) { }

}

9. Con la ayuda del @HostListener, utilizamos un click event para capturar el acto del usuario de hacer clic sobre el menú.

10. Creamos un evento al que llamamos toggleMenu, por ejemplo, que se encargará de quitar o añadir la clase show según el usuario haga clic en el botón para mostrar el menú. Es decir, la irá alternando: si el menú no se ve, añadirá la clase show, si se ve, la quitará.

Esta comprobación la hacemos con un bloque de if / else statement. 

Como lo que queremos que suceda al hacer clic es alternar el estado de la propiedad isOpen, se lo indicamos.

  @HostListener('click')
  toggleMenu() {
    if (!this.isOpen) {
      // show the menu
    } else {
      // hide the menu
    }
    // toggle the property
    this.isOpen = !this.isOpen;

  }

Para acceder al DOM, utilizamos el renderer, que nos ofrece métodos como el addClass o el removeClass. Los parámetros para estos métodos son:

  el elemento HTML sobre el que queremos añadir o quitar la clase

  la clase de CSS que queremos quitar o añadir (show)

En cuanto al elemento HTML, podemos acceder a él con el eleRef, pero al que queremos acceder es a su hermano inmediato, es decir, al <div> hermano del <a>: 

        <a class="nav-link dropdown-toggle" href="#" role="button" data-toggle="dropdown" appDropdown>
            Manage
        </a>
        <div class="dropdown-menu dropdown-menu-right">
          <a class="dropdown-item" href="#">Save data</a>
          <a class="dropdown-item" href="#">Fetch data</a>
        </div>

Porque ese es el verdadero menú desplegable. El <a> sólo contiene el botón para desplegarlo.

Al hermano inmediato del elemento podemos acceder usando nextElementSibling. 👯

    if (!this.isOpen) {
      // show the menu
      this.renderer.addClass(this.eleRef.nativeElement.nextElementSibling, 'show');
    } else {
      // hide the menu
      this.renderer.removeClass(this.eleRef.nativeElement.nextElementSibling, 'show');
    }

¡Y ya lo tenemos! ✌ Sólo nos queda añadir la directiva donde la necesitemos, es decir, en el botón Manage recipe, que está en el recipe-detail.component.html.

<button class="btn btn-primary dropdown-toggle" type="button" 
        id="dropdownMenuButton" data-toggle="dropdown"
        aria-haspopup="true" aria-expanded="false" appDropdown>
        Manage recipe
</button>

¡Perfecto! 😁

Como toque final, podríamos hacer que el menú pudiese cerrarse al hacer clic en cualquier lugar de la página, no sólo en el botón. Para eso, podemos manipular el DOM un poquito más, así: 

    document.addEventListener('click', event => {
      if (event.target !== this.eleRef.nativeElement) {
        this.isOpen = false;
        this.renderer.removeClass(this.eleRef.nativeElement.nextElementSibling, 'show');
      }
    });

    // toggle the property
    this.isOpen = !this.isOpen;

¡Y ahora si que sí! 🥳

¡BRAVO!

¡Y con esto terminamos la 3ª parte de este proyecto! Espero que hayas aprendido algo nuevo 😊.  Si te queda alguna duda, ¡nos vemos en los comentarios! Y si quieres seguir aprendiendo, ¡nos vemos en la parte #4!

Sobre la autora de este post

Soy Rocío, una abogada reconvertida en programadora. Soy una apasionada de aprender cosas nuevas y ferviente defensora de que la única manera de ser feliz es alcanzando un equilibrio entre lo que te encanta hacer y lo que te saque de pobre. Mi historia completa, aquí. 

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:

angular the complete guide - curso Max S.

  Max Schwarzmüller

curso angular fernando herrera

   Fernando Herrera

Si necesitas apoyo en forma de libro, puede que éstos te sirvan de ayuda:

libro 1 angular
libro 2 angular

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 😊.

Otros artículos que pueden interesarte

Cómo aprendí a programar cuando estaba «programada» para ser de letras
[tcb-script src="https://player.vimeo.com/api/player.js"][/tcb-script]A nadie le gusta su trabajo. Eso es lo que me decía a mí misma cuando conseguí mi primer[...]
Días del 160 al 203 – ¡Primer objetivo conseguido!
“A veces podemos pasarnos años sin vivir en absoluto, y de pronto toda nuestra vida se concentra en un solo[...]
Claves para entender Angular. Qué es y cómo se utiliza
Angular es un framework creado por Google que nos permite construir Single Page Applications (SPA, por sus siglas en inglés).Frameworks¿Pero qué es[...]
Si crees que este post puede serle útil a alguien, ¡compártelo!:

Deja un comentario

Este sitio usa Akismet para reducir el spam. Aprende cómo se procesan los datos de tus comentarios.

Esta web utiliza cookies para asegurar que se da la mejor experiencia al usuario. Si continúas utilizando este sitio se asume que estás de acuerdo. más información

Los ajustes de cookies en esta web están configurados para «permitir las cookies» y ofrecerte la mejor experiencia de navegación posible. Si sigues usando esta web sin cambiar tus ajustes de cookies o haces clic en «Aceptar», estarás dando tu consentimiento a esto.

Cerrar