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.
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.
# 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.
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?
.
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.
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.
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
.
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