ruby-on-railsturbo-rails

Multiple Partial Updates in Rails using Turbo Stream


I have two models, one for customer and one for complaints, one customer also has many complaints. If a complaint is added to the customer, how can you use Turbo Stream to update both the index of complaints and the show view of the customer using two different partials while ensuring only the specific customer's complaints are listed in the customer show view?

Here is some of my code:

Customer model:

class Customer < ApplicationRecord
    validates_presence_of :names
    belongs_to :company
    has_many :complaints

    broadcasts_to ->(customer) { [customer.company, "customers"] }, inserts_by: :prepend
end

Complaint model:

class Complaint < ApplicationRecord
    belongs_to :company
    belongs_to :customer

    broadcasts_to ->(complaint) { [transgression.company, "complaints"] }, inserts_by: :prepend
end

Complaints index.html.erb (streaming fine):

<%= turbo_stream_from @company, "complaints" %>

<h1>Complaints</h1>


<%= link_to new_complaint_path, class: "btn", data: { turbo_frame: "remote_modal" } do %>
  New
<% end %>
<div class="table-responsive">
  <table class="table">
    <thead>
      <tr>
        <th scope="col">Customer</th>
        <th scope="col">Date</th>
        <th scope="col">Note</th>
        <th colspan="2"></th>
      </tr>
    </thead>
    <tbody id="complaints">
      <%= render @complaints %>
    </tbody>
  </table>

Customer show.html.erb view (not streaming...):

<%= turbo_stream_from @customer %>
<%= turbo_stream_from @customer, "complaints" %>

  <%= turbo_frame_tag "customer" do %>
    <%= @customer.names %>
  <% end %>

  <%= link_to edit_employee_path(@customer), :class => "btn", data: { turbo_frame: "remote_modal" } do %>
    Edit
  <% end %>

  <h4>Complaints</h4>

  <%= link_to new_complaint_path, class: "btn", data: { turbo_frame: "remote_modal" } do %>
    Add
  <% end %>

  <div class="table-responsive">
    <table class="table">
      <thead>
        <tr>
          <th scope="col">Date</th>
          <th scope="col">Note</th>
          <th colspan="2"></th>
        </tr>
      </thead>
      <tbody id="complaints">
        <%= render @complaints %>
      </tbody>
    </table>
  </div>

Solution

  • On customer show page you're subscribing to @customer, "complaints" but you're not broadcasting anything to it, so there are no updates.

    # app/models/complaint.rb
    
    class Complaint < ApplicationRecord
        belongs_to :company
        belongs_to :customer
    
        # NOTE: this will send turbo streams to `[company, "complaints"]`
        #       you have to be subscribed to that channel to receive updates.
        # Defaults:
        #       partial: "complaints/complaint"
        #       target:  "complaints"
        broadcasts_to ->(complaint) { [complaint.company, "complaints"] }, inserts_by: :prepend
    
        # NOTE: to receive updates on the customer show page you have send
        #       updates to [customer, "complaints"]
        broadcasts_to ->(complaint) { [complaint.customer, "complaints"] },
          inserts_by: :prepend,
          partial: "customers/complaint" # <= if you want to use a different partial
    end
    
    # app/views/customers/show.html.erb
    
    # make sure to subscribe to a channel that you're broadcasting to
    <%= turbo_stream_from @customer, "complaints" %>
    
    # you have to have an `id` target for the incoming `turbo_stream`
    <div id="complaints">
      # render a different partial
      <%= render partial: "customers/complaint", collection: @complaints %>
    </div>
    

    I'm not sure what issues you have in the controllers, you don't need to do anything for broadcasts to work:

    # app/controllers/customers_controller.rb
    
    # GET /customers/1
    def show
      # NOTE: only show specific customer complaints on initial request
      #       broadcasts are already limited to `@customer`
      @complaints = @customer.complaints
    end