Pretty simple question - I find the *.turbo_stream.erb files pretty intuitive for updating the current user’s content.
However, updating everyone in the same way is pretty cumbersome. Is it possible to render a turbo_stream template file, and broadcast it for some identifier?
This is, for all intents and purposes, the exact same code, written 2 different ways, in 2 different places. Right now, I can stick the second one in an after_update_commit and everything will work just fine. Can I do the same thing somehow with the template file?
In addition to the four basic actions, you can also use broadcast_render_later or broadcast_render_later_to to render a turbo stream template with multiple actions.
Basically - from the model - I’m just rendering template with multple actions to update multiple page elements.
Thanks @tleish, I’m aware of the basics of broadcasting but I’m specifically referring to broadcast_render_later that none of those articles seem to cover. The docs strangely omit an actual example of usage, thus my question to @Audrius.
Note that rendering a turbo-stream inline will cause template rendering to happen synchronously. That is usually not desirable for model callbacks, certainly not if those callbacks are inside of a transaction. Most of the time you should be using broadcast_render_later, unless you specifically know why synchronous rendering is needed.
def broadcast_render_later_to(*streamables, **rendering)
Turbo::Streams::BroadcastJob.perform_later stream_name_from(streamables), **rendering
end
# The job that powers the <tt>broadcast_render_later_to</tt> available in <tt>Turbo::Streams::Broadcasts</tt> for rendering
# turbo stream templates.
class Turbo::Streams::BroadcastJob < ActiveJob::Base
discard_on ActiveJob::DeserializationError
def perform(stream, **rendering)
Turbo::StreamsChannel.broadcast_render_to stream, **rendering
end
end
You would then need to process the job queue in order to render the template (if not running the job service).
**rendering gets passed to the render method but the partial to render is set to the models partial_path by default so if you want to render something custom it only supports partial: and html: which had to be added in as a special edge case.
// from turbo_rails/models/concerns/turbo/broadcastable.rb
def broadcast_rendering_with_defaults(options)
options.tap do |o|
# Add the current instance into the locals with the element name (which is the un-namespaced name)
# as the key. This parallels how the ActionView::ObjectRenderer would create a local variable.
o[:locals] = (o[:locals] || {}).reverse_merge!(model_name.element.to_sym => self)
# if the html option is passed in it will skip setting a partial from #to_partial_path
unless o.include?(:html)
o[:partial] ||= to_partial_path
end
end
end
**rendering gets passed into this method as options
Ah righty, I was hoping it would allow rendering a whole response template like create.turbo_stream.erb etc… I have some quite complex streams for reacting to when things get updated but also reordered etc… I suppose I could just move a lot of that to their own partials that can be referred to from both the template and the model callback. Thanks for your help!