Copiando sem recarregar a página

Prévia grátis 4 min de leitura

Um botão que não muda nada na página além da área de transferência

O gerenciador de imagens de uma aula mostra uma URL de imagem gerada ao lado de um pequeno ícone de copiar. Clicar no ícone coloca a URL na área de transferência, um toast confirma, e nada mais na página se mexe. Nenhum formulário é enviado, nenhuma requisição vai ao servidor e nenhuma navegação do Turbo acontece. O recurso inteiro mora num arquivo pequeno, o app/javascript/controllers/ui/clipboard_controller.js deste app:

import { Controller } from "@hotwired/stimulus"

export default class extends Controller {
    static targets = ["source"]
    static values = { message: String }

    copy(event) {
        if (event) {
            event.preventDefault()
        }

        const text = this.sourceTarget.value || this.sourceTarget.textContent

        if (this.sourceTarget.select) {
            this.sourceTarget.select()
        }

        // navigator.clipboard is undefined outside a secure context and in
        // browsers without the async Clipboard API. Calling writeText on it
        // would throw before any promise exists, skipping the fallback below,
        // so turn that case into a rejection the .catch() can handle.
        const write = navigator.clipboard?.writeText
            ? navigator.clipboard.writeText(text)
            : Promise.reject(new Error("Clipboard API unavailable"))

        write.then(() => {
            this.showToast()
        }).catch(err => {
            console.error('Failed to copy text: ', err)
            try {
                // execCommand reports failure by returning false, not by throwing.
                if (document.execCommand('copy')) this.showToast()
            } catch (err) {
                console.error('Fallback: Oops, unable to copy', err)
            }
        })
    }

    showToast() {
        // builds and appends a toast element; lesson 6 covers this method in full
    }
}

Tudo acima bate com o arquivo deste app, exceto o corpo do showToast, cortado aqui (o comentário marca o corte) porque a aula 6 o cita completo. Este curso lê o resto linha por linha e depois segue o controller até as três páginas que o ligam ao HTML.

Por que isso precisa de JavaScript

Um link <a href="..."> comum muda a página, e um submit de formulário também. Escrever na área de transferência é diferente: é um recurso do navegador sem elemento HTML próprio, não existe tag <clipboard>. Algo precisa rodar no navegador em resposta a um clique e chamar uma API do navegador. É esse o trabalho do Stimulus aqui: prender uma classe pequena a um pedaço do DOM e dar a ela uma coisa para fazer no clique.

O que é um controller do Stimulus, nos termos deste arquivo

export default class extends Controller é o formato inteiro. Controller, importado de @hotwired/stimulus, é a classe base que todo controller do Stimulus estende. static targets = ["source"] diz que o controller espera um elemento com nome dentro dele, chamado source: o elemento cujo texto é copiado. static values = { message: String } lê da página o texto do toast, para que cada página passe uma mensagem traduzida (aula 6). copy(event) é um método de instância comum, chamado quando a ligação da aula 3 dispara um clique. Nada aqui é específico do Rails nem JavaScript incomum. É uma classe pequena com um método público, importada e registrada como todo controller em app/javascript/controllers/.

O que este curso cobre

A aula 2 abre o HTML que liga este controller a uma página: os atributos data-controller e data-*-target. A aula 3 trata do data-action, incluindo uma diferença entre dois elementos da mesma marcação que vale entender. A aula 4 trata do navigator.clipboard.writeText(), a API moderna de Clipboard que este controller chama primeiro. A aula 5 trata do fallback document.execCommand('copy'): por que ele está obsoleto, por que o código ainda o chama e como o código garante que ele tenha chance de rodar. A aula 6 abre o showToast() completo, um método que monta um toast em HTML e reaproveita o comportamento de dispensa de outro controller. A aula 7 lê as outras páginas que usam este controller e fecha o curso.

Aula 1 de 7 · grátis

Continue com a aula 2

Targets: conectando o elemento source

Mostra como data-controller e data-ui--clipboard-target prendem o controller e geram o this.sourceTarget do input.

Com uma conta grátis você também tem os quizzes, seu progresso e suas anotações, e a discussão.

Digite para buscar...