Vários frames, uma página
No curso de nível Júnior "Hotwire: Turbo Drive & Frames", cada exemplo de frame tinha uma única função na página: a seção de imagens do editor de aulas, a thread de discussão. Nada diz que uma página só pode ter um. A lista de cursos do admin desta aplicação tem quatro frames independentes na mesma página, um por nível, e cada um pagina só a si mesmo.
Quatro frames, um por nível
app/views/admin/catalog/courses/index.html.erb, resumido: a tabela dentro de cada frame virou um comentário:
<% @result.levels_data.each do |level_name, data| %>
<% pagy = admin_paginator(total_count: data.total_count, page_key: "#{level_name}_page", per_page: @per_page) %>
<%= turbo_frame_tag "level-#{level_name}", data: { turbo_action: "advance" } do %>
<div id="level-<%= level_name %>" class="mb-10 scroll-mt-24">
<!-- tabela de cursos para este nível -->
<% end %>
<% end %>
Um único each sobre quatro níveis (intern, junior, mid, senior) gera quatro elementos <turbo-frame> separados: level-intern, level-junior, level-mid e level-senior. Cada id vem do nome do nível, e cada frame envolve a própria tabela paginada. page_key: "#{level_name}_page" faz com que clicar em "próxima página" na tabela de intern não mexa no número de página da tabela de junior.
Por que um id dinâmico
Escrever turbo_frame_tag "level-intern", turbo_frame_tag "level-junior" e os demais à mão funcionaria igual. O "level-#{level_name}" dinâmico dentro de um loop é só DRY, o mesmo motivo de percorrer qualquer coleção em vez de repetir o código. O que importa é o que o id oferece. Um link de paginação gerado dentro do frame level-mid aponta para level-mid por padrão (o alvo restrito ao frame, regra daquele curso de nível Júnior). Então clicar em "próxima página" na tabela de nível mid troca só o conteúdo desse frame. As tabelas de intern, junior e senior ao lado não recarregam, não piscam e mantêm a própria paginação.
O que quebra se dois frames dividem um id
Um <turbo-frame id="..."> precisa ser único na página, como qualquer id de HTML. O Turbo depende disso em dois pontos. Quando uma resposta chega, ele copia o conteúdo do primeiro turbo-frame#id que encontra no HTML novo. Quando um link aponta um frame com data-turbo-frame, o Turbo o procura com document.getElementById, que devolve o primeiro elemento com aquele id. Então, se dois frames dividem um id, por um bug num loop ou por uma partial copiada com o id fixo, o segundo frame recebe o conteúdo do primeiro, e um link que mira o segundo atualiza o primeiro. Nada levanta erro. A página só mostra a tabela errada.
A view citada tem um caso menor disso. Cada frame envolve uma <div id="level-<%= level_name %>"> com o mesmo id do frame. Isso é HTML inválido, mas aqui não quebra nada. Um link de paginação dentro de um frame aponta para esse frame sem procurar id nenhum, e a busca na resposta pede turbo-frame#level-mid, que ignora a div. Os ids por nível continuam importando: os quatro níveis nunca colidem entre si, porque o nome do nível faz parte de cada id.
Pontos-chave
- Nada limita uma página a um turbo frame. O índice de cursos do admin desta aplicação renderiza quatro, um por nível, com um único loop.
turbo_frame_tag "level-#{level_name}"gera um id diferente a cada volta. A paginação de cada frame (page_key: "#{level_name}_page") é contada à parte, então paginar a tabela de um nível nunca mexe na de outro.- O ganho de um id por nível é o alvo padrão: um link de paginação renderizado dentro do frame de um nível aponta para esse frame e deixa os outros da página em paz.
- Ids de frame precisam ser únicos na página. Com dois frames dividindo um id, o Turbo usa a primeira ocorrência, e um frame recebe em silêncio o conteúdo do outro. A
<div>interna desta view repete o id do frame, o que é HTML inválido, mas aqui não faz mal.
Continue com a aula 2
Saindo com turbo-frame: _top
Como o alvo reservado _top leva um link ou formulário dentro de um frame para a página inteira.
Com uma conta grátis você também tem os quizzes, seu progresso e suas anotações, e a discussão.