DEV Community

Gabriel Gomes de Oliveira
Gabriel Gomes de Oliveira

Posted on • Updated on

Stimulus é um tesão (ou Como fazer uma navbar responsiva em Rails 7 e Bulma CSS)

Esse post é bem rápido e simples, pra seguir ele é necessário conhecimento básico em Ruby on Rails. Se você já manja muito de Rails e Stimulus, esse post não é para você! (por isso a #beginners XD).

De toda forma, gostaria de deixar registrado como aprendi a fazer uma simples navbar responsiva para um projetinho Rails 7 em que estou trabalhando.

Configurações

Ruby 3.3.0
Rails 7.1.3
gem bulma-rails na versão 0.9.4
gem "stimulus-rails"

Pequenos detalhes

Pra esse exemplo estou usando o código "Basic navbar" do Bulma CSS.

Vídeo da navbar sendo aberta e fechada na versão mobile

O problema que eu quero resolver com esse post é o uso de javascript no projeto Rails.
Para abrir e fechar a navbar, a documentação do Bulma CSS sugira que eu use esse código:

document.addEventListener('DOMContentLoaded', () => {

  // Pega todos os elementos "navbar-burger"
  const $navbarBurgers = Array.prototype.slice.call(document.querySelectorAll('.navbar-burger'), 0);

  // Adiciona o evento de click em cada um deles
  $navbarBurgers.forEach( el => {
    el.addEventListener('click', () => {

      // Pega o alvo a partir do atributo "data-target" que vem no navbar
      const target = el.dataset.target;
      const $target = document.getElementById(target);

      // Alternar a classe "is-active" do Bulma CSS tanto no "navbar-burger" e no "navbar-menu"
      el.classList.toggle('is-active');
      $target.classList.toggle('is-active');

    });
  });

});
Enter fullscreen mode Exit fullscreen mode

A questão é que eu estava tendo dificuldades para adicionar isso na minha aplicação e sempre que eu trocava de rota, a navbar parava de abrir. O código só funcionava uma vez.
Quando perguntei no grupo do Telegram Ruby Brasil a respeito, me foi sugerido que eu fizesse um "controller do Stimulus"
Eu já tinha ouvido falar a respeito dessa ferramenta, mas ainda não tinha tentado usá-la; então fui aprender.

Stimulus

Sem me aprofundar muito no assunto, o Stimulus é uma "framework de Javascript modesta para o HTML que você já tem". Ou seja, ele é uma ferramenta que te ajuda a manipular seu documento HTML usando javascript e alguns atributos especiais nas tags HTML.

Como eu quero fazer um controller relacionado ao navbar, eu criei dentro da pasta app/javascript/controllers o arquivo navbar_controller.js.
Inicializei ele da forma padrão:

import { Controller } from "@hotwired/stimulus"

export default class extends Controller {}
Enter fullscreen mode Exit fullscreen mode

Depois disso, criei uma partial navbar com o código do Basic Navbar do Bulma CSS

app/views/layouts/_navbar.html.erb

<nav class="navbar is-transparent container is-max-desktop">
  <div class="navbar-brand">
    <a class="navbar-item" href="/">
      <img src="https://bulma.io/images/bulma-logo.png" alt="Bulma: a modern CSS framework based on Flexbox" width="112" height="28">
    </a>
    <div class="navbar-burger">
      <span></span>
      <span></span>
      <span></span>
    </div>
  </div>

  <div class="navbar-menu">
    <div class="navbar-start">
      <a class="navbar-item" href="/">
        Início
      </a>
      <a class="navbar-item" href="/books">
        Catálogo
      </a>
      <a class="navbar-item" href="/">
        Assinatura
      </a>
      <a class="navbar-item" href="/books/new">
        Adicionar livro
      </a>
    </div>

    <div class="navbar-end">
      <div class="navbar-item">
        <div class="field is-grouped">
          <p class="control">
            <a class="bd-tw-button button">
              <span>
                Entrar
              </span>
            </a>
          </p>
          <p class="control">
            <a class="button is-primary">
              <span>Criar conta</span>
            </a>
          </p>
        </div>
      </div>
    </div>
  </div>
</nav>
Enter fullscreen mode Exit fullscreen mode

O que precisamos fazer é adicionar a classe "is-active" à div com a classe "navbar-menu" quando a div "navbar-burger" receber um click, e depois remover a classe "is-active" quando o burger receber outro click.
Dessa forma, precisamos estabelecer para o navbar_controller qual é a "área" que ele vai atuar, ou seja, qual elemento vai ser controlado ou gerenciado.
Para isso, colocamos o atributo data-controller="navbar" em nosso navbar, ficando assim:

<nav class="navbar is-transparent container is-max-desktop" data-controller="navbar">

Depois, precisamos definir qual elemento vai possuir a ação gerenciada pelo controller. Como queremos que seja o navbar-burger, colocamos um atributo chamado data-action, cujo valor tem o seguinte formado: ação->controller#função
O navbar burger fica assim:

<div class="navbar-burger" data-action="click->navbar#toggle">

E então precisamos ver qual é o elemento que será afetado por essa ação. Nosso "alvo" é o menu em si. Então, devemos colocar o atributo data-controller-target, substituindo "controller" pelo nome dado ao nosso controller. Ou seja:

<div class="navbar-menu" data-navbar-target="menu">

Mas aí você se pergunta: que diabo de função é essa?
E eu te respondo!
Lá no nosso navbar_controller.js, vamos adicionar a função toggle(), que é a função que vai ser ativada quando houver um click no navbar-burger.
A função vai pegar o elemento "alvo" (target) e fazer a alternação na classe "is-active". Só que para pegar nosso elemento alvo, precisamos registrá-lo.

app/javascript/controllers/navbar_controller.js

import { Controller } from "@hotwired/stimulus"

export default class extends Controller {
    static targets = ["menu"];
}
Enter fullscreen mode Exit fullscreen mode

Todos nossos targets devem ser registrados dentro desse array, passando o valor que colocamos no atributo "data-navbar-target".
Dessa forma, conseguiremos acessar nosso target a partir da expressão this.menuTarget.
Assim, conseguiremos acessar toda a estrutura HTML desse elemento, podendo inclusive manipular suas classes! E, portanto, resolvendo nosso problema:

import { Controller } from "@hotwired/stimulus"

export default class extends Controller {
    static targets = ["menu"];

    toggle() {
        this.menuTarget.classList.toggle('is-active');
    }
}
Enter fullscreen mode Exit fullscreen mode

Com isso, conseguimos reduzir todas aquelas 7 linhas de JS puro em 3 atributos HTML e uma função simples com Stimulus.
Eu achei bem divertido fazer isso, é um código extremamente simples e fico imaginando as diversas possibilidades mais complexas as quais essa ferramenta pode nos ajudar a atuar.
Espero que essa publicação seja útil a alguém e é isso!
Para mais exemplos de Stimulus recomendo fortemente sua documentação e as aulas do The Odin Project!

Top comments (0)