connect(): onde o trabalho real de um controller começa

Prévia grátis 4 min de leitura

Uma classe de controller do Stimulus existe assim que o arquivo carrega: todo método está definido, toda declaração static foi lida. Isso não quer dizer que o controller já esteja fazendo algo. O connect() é o método que o Stimulus chama quando um elemento com o identificador desse controller aparece no DOM. O controller de gráficos desta aplicação usa esse momento para a única coisa que não podia acontecer antes.

Um controller que só começa no connect()

app/javascript/controllers/ui/chart_controller.js, o connect() e o render():

export default class extends Controller {
    static values = { type: { type: String, default: "bar" }, data: Object, options: Object }
    static targets = ["canvas"]

    connect() {
        this.disconnected = false
        this.render()
    }

    async render() {
        const { default: Chart } = await import("chart.js/auto")
        // The element can be gone by the time the import resolves.
        if (this.disconnected || !this.hasCanvasTarget) return

        const ctx = this.canvasTarget
        this.chart = new Chart(ctx, { type: this.typeValue, data: this.dataValue, options: { ...this.optionsValue } })
    }
}

O connect() chama this.render(), que lê this.canvasTarget, o elemento <canvas> a que o controller está ligado. O connect() só roda quando esse elemento já existe, como a abertura disse. Mas o render() é async. Ele importa o Chart.js de forma dinâmica, uma escolha proposital: a biblioteca tem cerca de 180 KB de JavaScript, fixada com preload: false, e só vale a pena numa página que desenha gráficos. Esse import pode demorar o bastante para o elemento se desconectar antes de terminar. A proteção if (this.disconnected || !this.hasCanvasTarget) return existe por um motivo. A garantia do connect() vale para o instante em que ele roda, e não para o que roda depois de um await.

Onde a aplicação guarda a resposta

O this.disconnected = false, no começo do connect(), e o seu espelho no disconnect() (próxima aula) são o jeito do próprio controller responder "ainda estou aqui?" depois de um await. O Stimulus não confere isso por você. O this.hasCanvasTarget é um helper gerado pelo Stimulus (existe um para cada target declarado) que vira false quando o elemento do target some. É a segunda metade da mesma proteção.

O que dispara o connect()

O Stimulus observa o DOM com um MutationObserver. Ele não fica consultando, não roda num timer e não depende do DOMContentLoaded. Quando um elemento com o atributo data-controller="chart" aparece em qualquer ponto do documento, o Stimulus cria o controller e chama o connect() dele. O elemento pode vir de um carregamento de página, da troca de um Turbo Frame ou de um turbo_stream.append (dois cursos atrás). Um controller conectado por uma atualização de Turbo Stream recebe o mesmo connect() que um presente desde o primeiro carregamento. Nesse instante, só importa que o elemento exista.

Por que isso é mais que outro método

O código que lê um target, um value ou qualquer outra coisa do elemento do controller precisa rodar dentro do connect() ou de algo que o connect() chame. Não pode ficar no corpo da classe nem numa instrução no topo do arquivo, porque nada no elemento está garantido antes disso. O chart_controller.js desta aplicação nunca lê this.canvasTarget fora do render(). E o render() só é chamado pelo connect() e, como as próximas aulas mostram, por callbacks de mudança de value que só rodam com um gráfico já existente. Mesmo ali ele confere se o elemento ainda está presente antes de mexer nele. O seu próprio await quer dizer que o tempo passou desde a última confirmação do connect().

Pontos-chave

  • O connect() roda quando o Stimulus detecta no DOM um elemento com o identificador data-controller do controller, e não quando o arquivo carrega ou a classe é definida.
  • O chart_controller.js desta aplicação chama this.render() no connect(), mas o render() é async. Ele importa o Chart.js de forma dinâmica, e quando o import termina o elemento pode ter sumido.
  • A proteção if (this.disconnected || !this.hasCanvasTarget) return acompanha o estado de conexão do controller através desse await. A garantia do connect() vale para o instante em que ele roda, e não para o que roda depois de uma promise aguardada.
  • O Stimulus encontra os elementos que se conectam com um MutationObserver, e não com consultas periódicas ou evento de carregamento. Um carregamento de página, a troca de um Turbo Frame e um append de Turbo Stream disparam o connect() do mesmo jeito.
Aula 1 de 7 · grátis

Continue com a aula 2

disconnect(): o espelho que desfaz connect()

O disconnect() desfaz o que o connect() começou, destruindo uma instância do Chart.js ou limpando o setTimeout do flash.

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

Digite para buscar...