Stimulus é um tesão (ou Como fazer uma navbar responsiva em Rails 7 e Bulma CSS)
Tags: braziliandevs, rails, stimulus, beginners
Published: 2024-03-01 00:20:31

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](https://github.com/joshuajansen/bulma-rails)
gem "stimulus-rails"

## Pequenos detalhes
Pra esse exemplo estou usando o código ["Basic navbar" do Bulma CSS](https://bulma.io/documentation/components/navbar/#basic-navbar).

![Vídeo da navbar sendo aberta e fechada na versão mobile](https://s9.gifyu.com/images/SF53o.gif "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:

```javascript
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');

    });
  });

});
```

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](https://t.me/rubybrasil) 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:

```javascript
import { Controller } from "@hotwired/stimulus"

export default class extends Controller {}
```

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

`app/views/layouts/_navbar.html.erb`
```html
<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>
```

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`
```javascript
import { Controller } from "@hotwired/stimulus"

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

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:

```javascript
import { Controller } from "@hotwired/stimulus"

export default class extends Controller {
    static targets = ["menu"];
    
    toggle() {
        this.menuTarget.classList.toggle('is-active');
    }
}
```

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](https://stimulus.hotwired.dev/handbook/hello-stimulus) e as aulas do [The Odin Project](https://www.theodinproject.com/lessons/ruby-on-rails-stimulus)!