Hi I started using hotwire in rails app (currently I am working on adding comments) and I found that partials used for turbo streaming are free of global references and I would like to use current_user in partial but then i had to reload page to add a comment. Did any of you have a similar problem and could you give me a hint how to do that?
I will provide an extensive response because this is what I wish I would have found when I didn't know the answer to this question. Here it goes:
Instead of user current_user, use Current.user. This is how it works:
class Current < ActiveSupport::CurrentAttributes
attribute :user
end
class ApplicationController < ActionController::Base
before_action :set_current_user, if: :user_signed_in?
private
def set_current_user
Current.user = current_user
end
end
Now inside your partials, remove all calls to current_user and use a local variable. So if you have current_user.posts, use user.posts. Inside any partial where you need to use current_user that is nested somewhere inside the views where you need to use Turbo Streams, use ONLY local variables and from your views you can call your partials passing:
user: Current.user.
In my case I needed to have Turbo Streams on my Roles, but I needed to pass the current user to my roles/_role partial, so:
<%= turbo_stream_from "roles" %>
<%= turbo_frame_tag "roles" do %>
<% @roles.each do |role| %>
<%= render "roles/role", role: role, user: Current.user %>
<% end %>
<% end %>
<%= turbo_frame_tag role do %>
<div class="role">
<%# Some stuff %>
<%= render "opportunities/form", role: role, user: user %>
<%# Some stuff %>
</div>
<% end %>
<% if user.opportunities.find_by(role_id: role.id) %>
<%# Some stuff %>
<% end %>
Notice how I ONLY use "user" (local variable) inside partials and from my views I pass down user: Current.user.
after_create_commit -> { broadcast_prepend_later_to "roles", partial: "roles/role", locals: { role: self, user: Current.user }, target: "roles" }
after_update_commit -> { broadcast_replace_later_to "roles", locals: { role: self, user: Current.user } }
after_destroy_commit -> { broadcast_remove_to "roles" }
For completeness sake:
Here is my controller (only the actions we need for this purpose):
class RolesController < ApplicationController
def create
@role = Role.new(role_params)
if @role.save
respond_to do |format|
format.html { redirect_to roles_path, notice: "Role created." }
format.turbo_stream
end
else
render :new, status: :unprocessable_entity
end
end
def update
@role = Role.find(params[:id])
if @role.update(role_params)
respond_to do |format|
format.html { redirect_to roles_path, notice: "Role updated." }
format.turbo_stream
end
else
render :edit, status: :unprocessable_entity
end
end
def destroy
@role = Role.find(params[:id])
@role.destroy
respond_to do |format|
format.html { redirect_to roles_path, notice: "Role deleted." }
format.turbo_stream
end
end
end
And my turbo_stream views:
<%= turbo_stream.prepend "roles", partial: "roles/role", locals: { role: @role, user: Current.user } %>
<%= turbo_stream.remove @role %>
I hope this helps.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With