Usando Stimulus para mostrar o nome da imagem no input de arquivo
Tags: rails, beginners, braziliandevs, stimulus
Published: 2024-03-28 23:26:31

Oi, mais um post rápido para ensinar o que acabei de aprender: a usar o Stimulus para conseguir mostrar ao usuário o nome do arquivo que ele está fazendo upload no input tipo 'file'.

![Input type file](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hc0y7w42y5th67gdtojr.png)

É bem chato não mostrar isso para o usuário e, apesar de ser algo bem simples de ser feito com Javascript puro, eu queria aprender como se faz usando o Stimulus!

Primeiro, adicionamos nosso `data-controller` na tag que engloba o nosso input. No caso, estou modificando um formulário de criação de livros na minha biblioteca pessoal


> `app/views/books/new.html.erb`
> ```html 
>   <div data-controller="file-input">
>     <label for="file">Escolha uma imagem para capa do livro</label>
>     <input type="file" id="file" name="file" />
>     <p>Nenhum arquivo selecionado...</p>
>   </div>
> ```


Depois, criamos o `file_input_controller` para manipular essa estrutura.


> `app/javascript/controllers/file_input_controller`
> ```javascript
> import { Controller } from "@hotwired/stimulus";
>
> export default class extends Controller {
> }
> ```


Precisamos então pensar quais são nossos "alvos", `targets`, para nossas ações. Queremos que nosso usuário veja o nome do arquivo selecionado após o input, então precisamos verificar o momento que ele faz essa seleção e também precisamos do local onde colocaremos a informação. Então, nossos `targets` são o input em si e o parágrafo logo em seguida, que mostra ou não se um arquivo foi escolhido.


> `app/views/books/new.html.erb`
> ```html 
>   <div data-controller="file-input">
>     <label for="file">Escolha uma imagem para capa do livro</label>
>     <input type="file" id="file" name="file" data-file-input-target="input" />
>     <p data-file-input-target="fileName">Nenhum arquivo selecionado...</p>
>   </div>
> ```


Criamos também as variáveis dos `targets` em nosso controller:


> `app/javascript/controllers/file_input_controller`
> ```javascript
> import { Controller } from "@hotwired/stimulus";
>
> export default class extends Controller {
>   static targets = ["input", "fileName"];
> }
> ```


Precisamos pensar agora em que momento queremos averiguar essa informação. Ou seja, quando o usuário vai querer ter essa interação com o formulário ou quando ela tem que estar disponível para ele? 

Oras, imediatamente, assim que a página é carregada, isso já precisa estar disponível funcionalmente.

Dessa forma, vamos chamar o método `connect()`, que é um [lifecycle callback](https://stimulus.hotwired.dev/reference/lifecycle-callbacks) do Stimulus, uma função nativa que vai ser chamada assim que o nosso controller for conectado com alguma parte da DOM.

Como nós queremos verificar se o usuário adicionou um arquivo pelo input, precisamos adicionar algo que observe qualquer mudança nesse elemento e modifique o texto com o nome do arquivo.

Assim, temos o seguinte:


> `app/javascript/controllers/file_input_controller`
> ```javascript
> import { Controller } from "@hotwired/stimulus";
>
> export default class extends Controller {
>     static targets = ["input", "fileName"];
>   
>     connect() {
>       this.inputTarget.addEventListener("change", this.updateFileName.bind(this));
>     }
>   
>     updateFileName() {      
>       if (this.inputTarget.files.length > 0) {
>         this.fileNameTarget.textContent = this.inputTarget.files[0].name;
>       }
>     }
> }
> ```

Explicando tudo, assim que o controller se conectar com o elemento DOM com o atributo `data-controller="file-input"`, ele vai buscar o `target` input (marcado com o atributo `data-file-input-target="input"`) e vai adicionar o "observador de eventos" de `change`, conectando o contexto do controller para a função `updateFileName`.

A função `updateFileName` vai ser ativada quando o valor do input for alterado, verificando se há algum arquivo e, se houver, irá alterar o conteúdo do elemento marcado com o `fileNameTarget` para o nome do arquivo. E voilà, é isso, acabou.

Parando pra pensar agora, acho que eu quero mostrar a imagem para o usuário também, mas isso eu faço outro dia! Abraço