A title for your blog

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.

#hotwire #phlex #rails #ruby #turbo