Skip to content

Instantly share code, notes, and snippets.

@javierav
Last active August 11, 2023 11:41
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save javierav/0061b996859378962f52a2579efd5246 to your computer and use it in GitHub Desktop.
Save javierav/0061b996859378962f52a2579efd5246 to your computer and use it in GitHub Desktop.

Hotwire Turbo

Drive

Intercepta los enlaces y el envío de formularios, los ejecuta en segundo plano y actualiza la página con la respuesta sin hacer una carga completa de toda la página. EL contenido de head se mezcla y el de body se sustituye.

Frames

Permite dividir una página en varias partes independientes y cargarlas en paralelo. Cualquier interacción (enlaces o formularios) que se realice dentro del frame, afectará sólo a ese frame, el resto de la página no cambia su contenido ni se recarga. Sirve para responder cuando la interacción la realiza el usuario.

Cómo funciona

# app/views/dashboard/index.html.erb
<%= turbo_frame_tag "messages", src: messages_path %>

# app/views/messages/index.html.erb
<%= turbo_frame_tag "messages" do %>
  <ul>
    <li>Message 1</li>
    <li>Message 2</li>
  </ul>
<% end %>

Cuando accedemos a la página del dashboard, el primer frame es renderizado en el navegador y al tener el atributo src definido, hará de forma separada una petición HTTP a ese endpoint añadiendo la cabecera HTTP Turbo-Frame: messages. Cuando Rails detecta esta cabecera, cambia el layout de nuestra aplicación por uno reducido que sólo renderiza la vista actual. En esa vista, se renderiza el mismo frame pero incluyendo el contenido del mismo. Turbo se encarga de reemplazar el contenido del viejo frame por el del nuevo.

Uso

Envío condicional de un frame

Una de las situaciones que se nos pueden dar es que el listado de mensajes que debemos devolver dentro de un frame para mostrar ese listado embebido en nuestro dashboard no tenga la misma estructura HTML que si accedemos al listado de mensajes de una forma tradicional por la url GET /messages.

Para resolver esto, tenemos disponible en el controlador el método turbo_frame_request?.

Streams

Al contrario que los frames, los streams permiten actualizar el contenido de cualquier parte de la página como respuesta a una petición del usuario o en segundo plano desde el servidor.

Cómo funciona

Cuando respondemos con uno o varios elementos turbo-stream en el HTML de respuesta, Turbo los procesa y realiza las acciones indicadas.

<turbo-stream action="append" target="messages">
  <template>
    <div id="message_1">
      This div will be appended to the element with the DOM ID "messages".
    </div>
  </template>
</turbo-stream>

En este ejemplo, Turbo buscará un elemento en nuestro HTML con el ID messages y le añadirá el HTML contenido dentro de la etiqueta template. Aparte de buscar por el ID podemos especificar cualquier selector CSS.

Las posibles acciones son: append, prepend, replace, update, remove, before, after.

Podemos enviar en una misma respuesta varios streams si necesitamos actualizar varias partes.

Uso

Como respuesta a una petición HTTP

Cuando se envía un formulario mediante POST, PUT, PATCH o DELETE, Turbo envia la petición con el mime type a text/vnd.turbo-stream.html. Esto nos va a pemitir responder con un stream desde el controlador.

# app/controllers/messages_controller.rb
def create
  @message = Message.create(create_params)
  
  respond_to do |format|
    format.turbo_stream
    format.html { redirect_to messages_url }
  end
end

# app/views/messages/create.turbo_stream.erb
<%= turbo_stream.append "messages", @message %>

<%= turbo_stream.replace "new_message" do %>
  <%= render partial: "new_message" %>
<% end %>

En este ejemplo, si se envía el formulario de forma normal se realizará una redirección al listado de mensajes, pero si se envía solicitando un stream, se devuelve los streams necesarios para añadir el nuevo mensaje al listado y resetear el formulario de añadir uno nuevo.

Por defecto Turbo no envía ese mime type como consecuencia de seguir enlaces o de enviar formularios GET. Si se quiere cambiar ese comportamiento, se puede añadir a la etiqueta a o form el atributo data-turbo-stream.

A través de websockets

Nos puede interesar actualizar el contenido de una página a todos aquellos usuarios que la están viendo en ese momento. Por ejemplo, si tenemos un listado de mensajes y un usuario añade uno nuevo, los demás usuarios no tendrán conocimiento de ello salvo que refresquen la página.

Para resolver este tipo de situaciones, podemos enviar streams a los usuarios cada vez que se produzca un evento en el servidor, por ejemplo cuando se crea un mensaje nuevo. Cuando eso sucede, podemos generar un stream para añadir ese mensaje al listado y enviarlo a todos los usuarios que estén viendo el listado de mensajes.

Para ello, tenemos que realizar dos acciones. La primera es suscribir al canal adecuado a cada uno de los usuarios que están visualizando el listado de mensajes mediante el helper turbo_stream_from. Por ejemplo:

# app/views/messages/index.html.erb
<%= turbo_stream_from :messages %>
<div id="messages">New messages will be appended to this target</div>

La segunda es renderizar el stream y enviarlo por websockets cuando se produzca un cambio en nuestro backend. Siguiendo con el ejemplo, debemos realizar la acción cuando se cree un nuevo mensaje.

# app/models/message.rb
class Message
  after_commit :update_messages_list
  
  def update_messages_list
    broadcast_append_later_to :messages
  end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment