I am creating an example project for my company and have to set up flagging for posts. I have used forms to setup post flagging method for users to flag posts in case of bad content. Here is the view
<div class="form-check form-switch">
<%= form_with url: toggle_flag_post_path(@post), method: :patch, data: { turbo: false } do |f| %>
<%= f.check_box :flag, {
class: "form-check-input toggle-flag-checkbox",
role: "switch",
data: { post_id: @post.id },
onchange: "this.form.submit()"
}, 1, 0 %>
<%= f.label :flag, (@post.flag ? "Unflag" : "Flag"), class: "form-check-label" %>
<% end %>
and here is the requisite controller method
def toggle_flag
@post.toggle!(:flag)
redirect_back(fallback_location: @post)
end
Is there a way to use Stimulus to toggle the flag without having to do a fullpage reload due to form.
You don't need Stimulus to do that. Rails has a convenient way to mutate parts of the page. It is called Turbo Streams. There are plenty tutorials on Turbo Streams in the web, here's a good one to start with.
So the steps are:
@post
to the form:<%= render "flag_form", post: @post %>
<div>
element with a unique ID:<div id="<%= dom_id post %>-form">
<!-- your form goes here -->
</div>
Enable turbo in your form: data: { turbo: true, turbo_stream: true }
Replace onchange: "this.form.submit()"
with onchange: "this.form.requestSubmit()"
(this.form.submit()
won't work with Turbo)
Remove the redirect_back
from the toggle_flag
action:
def toggle_flag
@post.toggle!(:flag)
end
Instead of redirecting back we'll mutate only a certain part of the page! This is there the magic of Turbo Streams comes into play.
toggle_flag.turbo_stream.erb
. Rails will automatically fallback to this view after executing the toggle_flag
action. In this template you can replace the form with the updated content:<%= turbo_stream.update "#{dom_id @post}-form" do %>
<%= render "flag_form", post: @post %>
<% end %>
This code will find a <div>
element with the provided ID (#{dom_id @post}-form
), and it will update content of the <div>
(i.e. it will re-render the form, since we have the render
statement inside the block), while the rest of the page will remain the same!