Hotwire update two partials for the same model on the same page

I’ve got an invoice model and it has many items. I would like to display half the invoice model at the top, then nest the items in the middle and have the other half of the invoice model displayed at the bottom.

When I update a nested item I can only get the top partial invoice parent to update. Is there a way to update the same model multiple times in the same page?

invoice show page;

<p id="notice"><%= notice %></p>

<%= turbo_stream_from @invoice %>

<%= turbo_frame_tag "invoice" do  %>
  <%= render @invoice %>

  <%= link_to 'Edit', edit_invoice_path(@invoice) %>
  <%= link_to 'Back', invoices_path, "data-turbo-frame": "_top" %>
<% end %>

<div id="items">
  <%= render @invoice.items %>
</div>

<%= turbo_frame_tag "new_item", src: new_invoice_item_path(@invoice), target: "_top" %>

<%= turbo_frame_tag "totals_invoice_#{@invoice.id}" do  %>
  <%= render partial: "totals", locals: { invoice: @invoice} %>
<% end %>

invoice model

class Invoice < ApplicationRecord
  has_many :items
  broadcasts
end

item model

class Item < ApplicationRecord
  belongs_to :invoice
  broadcasts_to :invoice

  after_update_commit do
    broadcast_replace_to :invoice,
                         partial: 'invoices/totals',
                         locals: { invoice: self.invoice },
                         target: "totals_invoice_#{self.invoice.id}"
  end

  validates_presence_of :description
  
  after_save :calc_total
  after_destroy :calc_total

  def calc_total
    subtotal = invoice.items.sum(:total)
    tax = subtotal * 10 / 100
    total = subtotal + tax
    
    invoice.update({
      subtotal: subtotal,
      tax: tax,
      total: total
    })
  end
end
1 Like

You’ll need to broadcast the update for both partials. I’ve never done this… but assuming your single broadcast is working as expected, the pseudo-code of what you need to do might look something like this?

  after_update_commit do
+   broadcast_replace_to :invoice,
+                        partial: 'invoices/invoice',
+                        locals: { invoice: self.invoice },
+                        target: "invoice"
    broadcast_replace_to :invoice,
                         partial: 'invoices/totals',
                         locals: { invoice: self.invoice },
                         target: "totals_invoice_#{self.invoice.id}"
  end

You are right. The code which worked in the end was;

  after_create_commit  do
    broadcast_action_later_to "invoice".try(:call, self) || send("invoice"),
      action: :append,
      target: :items
  end
 
  after_update_commit  do
    broadcast_replace_later_to "invoice".try(:call, self) || send("invoice")
  end
  
  after_update_commit  do
    broadcast_replace_later_to "invoice".try(:call, self) || send("invoice"),
      partial: 'invoices/totals',
      locals: { invoice: self.invoice },
      target: "totals_invoice_#{self.invoice.id}"
  end
  
  after_destroy_commit do
    broadcast_remove_to "invoice".try(:call, self) || send("invoice")
  end
  
  after_destroy_commit do
    broadcast_remove_to "invoice".try(:call, self) || send("invoice"),
      target: "totals_invoice_#{self.invoice.id}"
  end
1 Like