Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Use current_user in hotwire partial

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?

like image 734
Bartosz Bronikowski Avatar asked Oct 25 '25 09:10

Bartosz Bronikowski


1 Answers

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:

  1. Create a model named Current. Be aware this model does not inherit from ApplicationRecord, but from ActiveSupport::CurrentAttributes. See more here in the Rails Docs.
app/models/current.rb
class Current < ActiveSupport::CurrentAttributes
  attribute :user
end
  1. Set the current_user to be the user property of Current in ApplicationController
app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
  before_action :set_current_user, if: :user_signed_in?

  private

  def set_current_user
    Current.user = current_user
  end
end
  1. 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:

app/views/roles/index.html.erb
<%= turbo_stream_from "roles" %>

<%= turbo_frame_tag "roles" do %>
  <% @roles.each do |role| %>
    <%= render "roles/role", role: role, user: Current.user %>
  <% end %>
<% end %>
app/views/roles/_role.html.erb
<%= turbo_frame_tag role do %>
  <div class="role">
    <%# Some stuff %>
    <%= render "opportunities/form", role: role, user: user %>
    <%# Some stuff %>
  </div>
<% end %>
app/views/opportunities/_form.html.erb
<% 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.

  1. Now inside the model where you want to have the turbo_streams you need to manually set each action you want to target. Here is how I did it:
app/models/role.rb
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):

app/controllers/roles_controller.rb
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:

app/views/roles/create.turbo_stream.erb
<%= turbo_stream.prepend "roles", partial: "roles/role", locals: { role: @role, user: Current.user } %>
app/views/roles/destroy.turbo_stream.erb
<%= turbo_stream.remove @role %>

I hope this helps.

like image 117
drjorgepolanco Avatar answered Oct 27 '25 00:10

drjorgepolanco