Closed garethbradley closed 1 year ago
Hi @garethbradley, I've done a bit of work with Turbo Streams broadcasting and there really isn't a method for communicating the reordering of existing items. You can emulate it by removing the item from the list then inserting it where it needs to be. Or you can just re-render the entire list after a reorder.
You're right in that the update_all
method just executes SQL so Rails doesn't know anything happened. You can actually broadcast from the controller instead of the model, and that's what I've settled on as I found the concept of broadcasting from the model too restrictive. It assumes a very simple use of your models, whereas you might have these things rendered differently all over the place. Your choice of course :D
Here's a sample of my update method for a collection of images:
def update
respond_with image do |format|
if image.update(image_params)
format.turbo_stream
format.json
if image.saved_change_to_position?
Turbo::StreamsChannel.broadcast_update_to online_newsletter, target: helpers.dom_id(article, :images),
html: render_to_string(partial: 'online_newsletters/articles/image', collection: images, formats: [:html])
else
Turbo::StreamsChannel.broadcast_replace_to online_newsletter, target: image,
html: render_to_string(partial: 'online_newsletters/articles/image')
end
else
format.html { render 'edit', status: :unprocessable_entity }
format.json { head :unprocessable_entity }
end
end
end
I'm using the responders
gem plus decent_exposure
so ignore those parts but the key is that I'm detecting if I've moved the current item and if so I'm just re-rendering the entire list to everyone (in the updated order). I'm also not using acts_as_list
for this reordering so you may have to detect the change in position slightly differently in your code.
Hope that helps! :)
You legend! Thanks so much for the nudge (actually, fairly large budge) in the right direction!
def create
requires_full_refresh = (timeline_item_params[:position].to_i <= @scenario.timeline_items.count)
@timeline_item = @scenario.timeline_items.create!(timeline_item_params)
respond_to do |format|
format.turbo_stream
format.html { redirect_to @scenario, notice: "Timeline item was successfully created." }
format.json do
if (requires_full_refresh)
@timeline_items = @scenario.timeline_items.order(position: :asc)
puts "FULL REFRESH"
Turbo::StreamsChannel.broadcast_update_to @scenario, target: 'timeline_items',
html: render_to_string(partial: 'timeline_items/timeline_item', collection: @timeline_items, formats: [:html])
else
puts "NO REFRESH"
end
render json: @timeline_item
end
end
end
Excuse the little debugging puts
in there, but it's working a treat.
Many thanks
Nice @garethbradley, always happy to help. I moved away from acts_as_list
for this mini-project using turbo streams and instead rolled my own ordering system: https://gist.github.com/brendon/d6cfd60cb5e70dc77a15a2476f04d279
I should turn that into a gem one day but for now it's there if you want to try it out. It tries to be way more strict on reordering within a transaction to avoid concurrency issues. Not sure if it's perfect though :)
I'll be sure to give it a test once I get a little traction on this project. But yes - release it! :D
Thanks again
Hey,
I'm seeing an issue whilst using acts_as_list with Turbo Streams (Rails 7). The inserted record is broadcast as expected, but the "shifted" records (whilst updated) are not broadcast.
To help add context, I'm writing an app that has a Scenario, and each Scenario has many TimelineItems. The timeline items can be moved around to reorder them.
I wonder if it's the "Update All" that's not triggering individual updates?