I made some change in edit page and then remove some pictures without refreshing page since turbo_frame, and if I click "update" the page refresh and the change in text are gone, it only actually update if I click for second time. For example, the original edit page looks like thisbefore click update. I removeed all pictures and changed description to "456" and clicked "update" the page just refreshed and stay in edit page, however the description stored back to "123" like this after click update. The the page actually update if I click "update" again, wondering how to fix this?
Expected result: remove the pictures without refreshing page and update the page with changed description content when click ""update.
I got this in terminal if I click "update" after remove pictures and made change in description
15:00:47 web.1 | Started PATCH "/products/11" for 127.0.0.1 at 2023-06-21 15:00:47 +0800
15:00:47 web.1 | Processing by ProductsController#update as TURBO_STREAM
15:00:47 web.1 | Parameters: {"authenticity_token"=>"[FILTERED]", "product"=>{"name"=>"demo", "description"=>"456", "pictures"=>["", "eyJfcmFpbHMiOnsibWVzc2FnZSI6IkJBaHBBa2dCIiwiZXhwIjpudWxsLCJwdXIiOiJibG9iX2lkIn19--78e77267b308507009d75e994fa41632650269e1", "eyJfcmFpbHMiOnsibWVzc2FnZSI6IkJBaHBBa2tCIiwiZXhwIjpudWxsLCJwdXIiOiJibG9iX2lkIn19--2e611899e93b50023cdcb9d0828ffb2cb429c42e", "eyJfcmFpbHMiOnsibWVzc2FnZSI6IkJBaHBBa29CIiwiZXhwIjpudWxsLCJwdXIiOiJibG9iX2lkIn19--32146a6e6e2158b3e9a3e566bb4b1eb2b810b9f6"]}, "commit"=>"Update Product", "id"=>"11"}
15:00:47 web.1 | Product Load (0.1ms) SELECT "products".* FROM "products" WHERE "products"."id" = ? LIMIT ? [["id", 11], ["LIMIT", 1]]
15:00:47 web.1 | ↳ app/controllers/products_controller.rb:65:in `set_product'
15:00:47 web.1 | DEPRECATION WARNING: config.active_storage.replace_on_assign_to_many is deprecated and will be removed in Rails 7.1. Make sure that your code works well with config.active_storage.replace_on_assign_to_many set to true before upgrading. To append new attachables to the Active Storage association, prefer using `attach`. Using association setter would result in purging the existing attached attachments and replacing them with new ones. (called from block in update at /home/cc/ror/hongda/app/controllers/products_controller.rb:41)
15:00:47 web.1 | TRANSACTION (0.1ms) begin transaction
15:00:47 web.1 | ↳ app/controllers/products_controller.rb:41:in `block in update'
15:00:47 web.1 | ActiveStorage::Blob Load (0.1ms) SELECT "active_storage_blobs".* FROM "active_storage_blobs" INNER JOIN "active_storage_attachments" ON "active_storage_blobs"."id" = "active_storage_attachments"."blob_id" WHERE "active_storage_attachments"."record_id" = ? AND "active_storage_attachments"."record_type" = ? AND "active_storage_attachments"."name" = ? [["record_id", 11], ["record_type", "Product"], ["name", "pictures"]]
15:00:47 web.1 | ↳ app/controllers/products_controller.rb:41:in `block in update'
15:00:47 web.1 | ActiveStorage::Blob Load (0.1ms) SELECT "active_storage_blobs".* FROM "active_storage_blobs" WHERE "active_storage_blobs"."id" = ? LIMIT ? [["id", 328], ["LIMIT", 1]]
15:00:47 web.1 | ↳ app/controllers/products_controller.rb:41:in `block in update'
15:00:47 web.1 | TRANSACTION (0.0ms) rollback transaction
15:00:47 web.1 | ↳ app/controllers/products_controller.rb:41:in `block in update'
15:00:47 web.1 | Completed 404 Not Found in 9ms (ActiveRecord: 0.4ms | Allocations: 4581)
15:00:47 web.1 |
15:00:47 web.1 |
15:00:47 web.1 |
15:00:47 web.1 | ActiveRecord::RecordNotFound (Couldn't find ActiveStorage::Blob with 'id'=328):
15:00:47 web.1 |
15:00:47 web.1 | app/controllers/products_controller.rb:41:in `block in update'
15:00:47 web.1 | app/controllers/products_controller.rb:40:in `update'
15:00:47 web.1 | Started GET "/products/11/edit" for 127.0.0.1 at 2023-06-21 15:00:47 +0800
15:00:47 web.1 | Processing by ProductsController#edit as HTML
15:00:47 web.1 | Parameters: {"id"=>"11"}
15:00:47 web.1 | Product Load (0.1ms) SELECT "products".* FROM "products" WHERE "products"."id" = ? LIMIT ? [["id", 11], ["LIMIT", 1]]
15:00:47 web.1 | ↳ app/controllers/products_controller.rb:65:in `set_product'
15:00:47 web.1 | Rendering layout layouts/application.html.erb
15:00:47 web.1 | Rendering products/edit.html.erb within layouts/application
15:00:47 web.1 | ActiveStorage::Attachment Exists? (0.2ms) SELECT 1 AS one FROM "active_storage_attachments" WHERE "active_storage_attachments"."record_id" = ? AND "active_storage_attachments"."record_type" = ? AND "active_storage_attachments"."name" = ? LIMIT ? [["record_id", 11], ["record_type", "Product"], ["name", "pictures"], ["LIMIT", 1]]
15:00:47 web.1 | ↳ app/views/products/_form.html.erb:26
15:00:47 web.1 | ActiveStorage::Attachment Load (0.1ms) SELECT "active_storage_attachments".* FROM "active_storage_attachments" WHERE "active_storage_attachments"."record_id" = ? AND "active_storage_attachments"."record_type" = ? AND "active_storage_attachments"."name" = ? [["record_id", 11], ["record_type", "Product"], ["name", "pictures"]]
15:00:47 web.1 | ↳ app/views/products/_form.html.erb:27
15:00:47 web.1 | ActiveStorage::Blob Load (0.1ms) SELECT "active_storage_blobs".* FROM "active_storage_blobs" WHERE "active_storage_blobs"."id" = ? LIMIT ? [["id", 330], ["LIMIT", 1]]
15:00:47 web.1 | ↳ app/models/product.rb:7:in `picture_as_thumbnail'
15:00:47 web.1 | ActiveStorage::VariantRecord Load (0.2ms) SELECT "active_storage_variant_records".* FROM "active_storage_variant_records" WHERE "active_storage_variant_records"."blob_id" = ? AND "active_storage_variant_records"."variation_digest" = ? LIMIT ? [["blob_id", 330], ["variation_digest", "6x8KFtM/8O22vFlQ4EVqBuGjN8Q="], ["LIMIT", 1]]
15:00:47 web.1 | ↳ app/models/product.rb:8:in `picture_as_thumbnail'
15:00:47 web.1 | Rendered products/_form.html.erb (Duration: 15.0ms | Allocations: 5039)
15:00:47 web.1 | Rendered products/edit.html.erb within layouts/application (Duration: 15.8ms | Allocations: 5186)
15:00:47 web.1 | Rendered /home/cc/.rvm/gems/ruby-3.2.1/gems/hotwire-livereload-1.2.3/app/views/hotwire/livereload/_head_action_cable.html.erb (Duration: 2.5ms | Allocations: 738)
15:00:47 web.1 | Rendered shared/_nav.html.erb (Duration: 0.1ms | Allocations: 8)
15:00:47 web.1 | Rendered layout layouts/application.html.erb (Duration: 31.4ms | Allocations: 14486)
15:00:47 web.1 | Completed 200 OK in 34ms (Views: 31.7ms | ActiveRecord: 0.7ms | Allocations: 15226)
15:00:47 web.1 |
15:00:47 web.1 |
15:00:47 web.1 | Finished "/cable" [WebSocket] for 127.0.0.1 at 2023-06-21 15:00:47 +0800
15:00:47 web.1 | Hotwire::Livereload::ReloadChannel stopped streaming from hotwire-reload
15:00:47 web.1 | Started GET "/cable" for 127.0.0.1 at 2023-06-21 15:00:47 +0800
15:00:47 web.1 | Started GET "/cable" [WebSocket] for 127.0.0.1 at 2023-06-21 15:00:47 +0800
15:00:47 web.1 | Successfully upgraded to WebSocket (REQUEST_METHOD: GET, HTTP_CONNECTION: Upgrade, HTTP_UPGRADE: websocket)
15:00:47 web.1 | Hotwire::Livereload::ReloadChannel is transmitting the subscription confirmation
15:00:47 web.1 | Hotwire::Livereload::ReloadChannel is streaming from hotwire-reload
These are the controller, model, routes, and views
controller
class Products::PicturesController < ApplicationController
include ActionView::RecordIdentifier
before_action :set_product
def destroy
@picture = @product.pictures.find(params[:blob_id])
@picture.purge_later
respond_to do |format|
format.html { redirect_to edit_product_path(@product) }
format.turbo_stream { render turbo_stream: turbo_stream.remove(dom_id(@picture, :picture))}
end
end
private
def set_product
@product = Product.find(params[:product_id])
end
end
views
<div>
<%= form.file_field :pictures, multiple: true %>
<% if @product.pictures.attached? %>
<% @product.pictures.each do |picture| %>
<div id="<%= dom_id(picture, :picture) %>">
<%= link_to image_tag(product.picture_as_thumbnail(picture)), picture %>
<%= link_to "remove picture", product_picture_path(@product, blob_id: picture.blob_id), data: { turbo_method: :delete} %>
</div>
<%= form.hidden_field :pictures, multiple: true, value: picture.signed_id %>
<% end %>
<% end %>
</div>
routes
Rails.application.routes.draw do
root 'products#home'
resources :products do
resources :pictures, only: [:destroy], module: :products
end
# Define your application routes per the DSL in https://guides.rubyonrails.org/routing.html
# Defines the root path route ("/")
end
model
class Product < ApplicationRecord
has_many_attached :pictures
def picture_as_thumbnail(pic)
return unless pic.content_type.in?(%W[image/jpeg image/png])
pic.variant(resize_to_limit: [200, 200]).processed
end
end