I want to click an SVG icon, trigger a Stimulus action which updates my model's (trx) "cleared" attribute on the backend, and then update the view to show a different SVG icon indicating the trx is cleared, without a page refresh.
Initially, my view partial displays the correct SVG depending on the model's "cleared" value (... if trx.cleared?).
app/views/trxes/_trx.html.erb
<div id="<%= dom_id trx %>" data-controller="trx-toggle" data-trx-toggle-url-value="<%= trx_path(trx) %>" data-trx-toggle-cleared-value="<%= trx.cleared %>">
...
<div>
<% if trx.cleared? %>
<%= inline_svg_tag "icons/cleared_closed.svg", class: "h-6 w-6 fill-green-500", aria_hidden: true, data: { action: "click->trx-toggle#toggleCleared", trx_toggle_target: "cleared"} %>
<% else %>
<%= inline_svg_tag "icons/cleared_open.svg", class: "h-6 w-6 fill-green-500", aria_hidden: true, data: { action: "click->trx-toggle#toggleCleared", trx_toggle_target: "cleared" } %>
<% end %>
</div>
...
</div>
Clicking the icon correctly triggers this Stimulus action which sends the opposite boolean value in the params
trx_toggle_controller.js
...
toggleCleared() {
let formData = new FormData()
formData.append("trx[cleared]", !this.clearedValue) // "true" or "false"
const token = document.getElementsByName("csrf-token")[0].content;
fetch(this.urlValue,{
body:formData,
method: 'PATCH',
dataType: 'script',
credentials: 'include',
headers: {
"X-CSRF-Token": token,
"Accept": "text/vnd.turbo-stream.html, text/html, application/xhtml+xml"
},
})
}
...
Which appears to send the correct params, and correctly updates the record (log output)
Started PATCH "/trxes/123" for 127.0.0.1 at 2023-04-07 22:31:38 -0700
Processing by TrxesController#update as TURBO_STREAM
Parameters: {"trx"=>{"cleared"=>"true"}, "id"=>"123"}
...
Trx Update (0.3ms) UPDATE "trxes" SET "updated_at" = ?, "cleared" = ? WHERE "trxes"."id" = ? [["updated_at", "2023-04-08 05:31:38.761532"], ["cleared", 1], ["id", 123]]
↳ app/controllers/trxes_controller.rb:45:in `block in update'
...
Rendered trxes/_trx.html.erb (Duration: 7.4ms | Allocations: 2233)
Completed 200 OK in 24ms (Views: 0.2ms | ActiveRecord: 5.2ms | Allocations: 8649)
app/controllers/trxes_controller.rb
def update
trx(new_trx_params)
respond_to do |format|
if trx.save!
format.turbo_stream { render turbo_stream: turbo_stream.replace(
helpers.dom_id(trx),
partial: 'trx',
locals: { trx: trx }
) }
format.html { render_to_string(partial: 'trx', locals: {trx:trx }) }
else
trx.errors.full_messages.each do |e|
puts "DEBUG Trx Error: #{e}"
end
render :new
end
end
end
The record updates, and a new JS request is visible on the NETWORK tab of my browser but I need to refresh the page to see the updated icon.
I have a slideover form for editing the entire trx record, and it works without a page refresh. The only difference I see is in the NETWORK tab: it shows a POST request initiated by turbo.min... with CONTENT-TYPE: application/x-www-form-urlencoded;charset=UTF-8
. Whereas my stimulus controller request is PATCH initiated by trx_toggle_controller... and CONTENT-TYPE: multipart/form-data; boundary=---------------------------419727981959891446676073942