Rendering Phlex Components with Turbo Streams (Solved)
After a lot of mucking about and digging through the source code for Turbo::Broadcastable I have finally figured out how to send a Phlex component to update a page element over a turbo steam web socket.
The key is the :renderable
option.
after_create_commit do
broadcast_prepend_to([participant, 'processed_files'],
target: 'processed_files',
renderable: ProcessedFiles::ProcessedFile.new(self))
end
The documentation for this is currently here Module: Turbo::BroadcastableβDocumentation for hotwired/turbo-rails (main). I was only able to find this after I discovered how to make this work. Even then itβs not clear that a Phlex component is a renderable.
Update 6 Jan 2025:
This has issues. It seems it doesn't play nice with Devise because you need a request object. I don't know if this an issue specific to Devise, Rails or the way I'm trying to use Turbo. I will keep this updated as I investigate.
Update 11 Jan 2025
After a lot of back and forth it turns out the above code does work. This is a model life cycle hook that triggers the web socket update. The code that doesnβt work is the following. This lives in a service that triggers the view update
Turbo::StreamsChannel.broadcast_prepend_to(
[participant, 'processed_files'],
target: 'processed_files_table',
renderable: ProcessedFiles::ProcessedFile.new(processed_file)
)
A bit more context to this. This is the process
βββββββββββββββ
βFile listenerβ
ββββββ¬βββββββββ
β
ββββββ½βββββββ
βSidekiq Jobβ
ββββββ¬βββββββ
β
ββββββ½ββββββββββββββββ
βFileProcessorServiceβ
ββββββ¬βββββββββ¬βββββββ
β β
β ββββββ½ββββββββββ
Turbostream β βProcessedFile β
β ββββββ¬ββββββββββ
β β Turbostream
ββ½βββββββββ½ββ
β UI β
βββββββββββββ
There are a lot of moving parts and any one of them might be causing the issue with Devise. Including the fact that I was firing off multiple broadcasts from different places in the code. I thought it might be trying to trigger UI updates in a job that might be the problem. But the broadcast in the lifecycle hook works. I could remove the turbo call in the service and just rely on the after commit hook, but I would prefer not to do this so that I can have better control over when UI updates happen.
Joel Drapper, the author of Phlex, discovered my plight on Bluesky and said that the above code should work. He suggested I try adding layout: false
to the broadcast arguments. And this fixed the problem. He also said that specifying a renderable
should do this automatically. I might dig into the code again and see why it isnβt set.
This is the final broadcast code I have in the services to update the UI via a turbo stream web socket.
Turbo::StreamsChannel.broadcast_prepend_to(
[participant, 'processed_files'],
target: 'processed_files_table',
renderable: ProcessedFiles::ProcessedFile.new(processed_file),
layout: false
)
This means I can remove the life cycle hooks and have explicit control over when the UI updates happen.