I'm running rails 7.0.4. I use turbostream to refresh a content of the corresponding view without reloading all page. Everything goes well if I refresh content inside <%= yield %> block (nested into application.html.erb). In my store progect I set a cart where I put items (products). carts/show.html.erb reflects all items I put to the cart. Also _header.html.erb, nested inside application template (application.html.erb), holds a link to the cart and an indicator of current items' quantity in the cart. I use turbostream in CartItemsController for dynamical content refresh inside corresponding view (app/views/carts/show.html.erb) and inside depending field of app/views/shared/_header.html.erb. If I delete, for instance, an item then turbo will correctly cust record out of page, but the ndicator of current items' quantity in the cart shows old quantity untill you refresh or redirect page. Terminal shows that turbo reload all related partials, but in fact only content of the <%= yield %> block is refreshing.
How to force turbo update content of the app/views/shared/_header.html.erb?
Here is some code:
# frozen_string_literal: true
class CartItemsController < ApplicationController
def create
@cart_item = CartItem.new(cart_item_params)
authorize cart_item
@cart_item.save if @cart_item.valid?
respond_to do |format|
format.turbo_stream do
render turbo_stream: [
turbo_stream.update("cart_item_form_#{@cart_item.product.id}", partial: 'products/cart_item_form',
locals: { product: @cart_item.product, products_in_cart: true })
]
end
end
end
def update
authorize cart_item
cart_item.update(cart_item_params)
end
def destroy
authorize cart_item
if cart_item.destroy
if cart_item.cart.cart_item_ids.any?
cart_sum
get_cart_quantity
cart_quantity = cart_item.cart.cart_items.count
respond_to do |format|
format.turbo_stream do
render turbo_stream: [
turbo_stream.remove(@cart_item),
turbo_stream.update(@cart_quantity, partial: 'shared/header', locals: {cart_quantity: cart_quantity}),
# turbo_stream.replace(:cart_quantity, cart_item.cart.cart_items.count),
# turbo_stream.replace(cart_quantity: cart_item.cart.cart_items.count),
turbo_stream.update('cart_sum', partial: 'carts/cart_sum')
]
format.html { render layout: 'shared/header' }
end
end
else
redirect_to cart_path(current_user.cart.id)
end
end
end
private
def cart_item
@cart_item ||= CartItem.find(params[:id])
end
def cart_sum
@cart_sum = 0
cart_item.cart.cart_items.map do |cart_item|
@cart_sum += cart_item.quantity * cart_item.product.price
end
end
def get_cart_quantity
@cart_quantity = cart_item.cart.cart_items.count
end
def authorize_cart_item
authorize cart_item
end
def cart_item_params
params.require(:cart_item).permit(:quantity, :cart_id, :product_id)
end
end
app/views/shared/_header.html.erb
<div class="mobile-menu">
</div>
<div class="header">
<div class="header-container">
<div class="gap-1"></div>
<div class="link-block">
<div><%= link_to t('.about_us'), '#', class: 'header-link' %></div>
<div><%= link_to t('.payment'), '#', class: 'header-link' %></div>
<div><%= link_to t('.contact'), '#', class: 'header-link' %></div>
<div><%= link_to t('.about_project'), '#', class: 'header-link' %></div>
</div>
<div class="gap-1"></div>
<div class="menu-item"><%= link_to t('.menu', default: 'no translate'), '#', class: 'header-link' %></div>
<div class="devise-menu">
<% if user_signed_in? %>
<div><%= link_to t('buttons.sign_out'), destroy_user_session_path, data: {turbo_method: :delete}, class: 'header-link' %></div>
<% else %>
<div><%= link_to t('buttons.sign_in'), new_user_session_path, method: :post, class: 'header-link' %></div>
<div class="vertical-rod"><span><%= ' | ' %></span></div>
<div><%= link_to t('buttons.sign_up'), new_user_registration_path, method: :post, class: 'header-link' %></div>
<% end %>
</div>
<div class="block-cart">
<% if user_signed_in? %>
<div id="cart">
<span id="cart-total">
<%= link_to (image_tag('icon_basket.png', alt: t('.cart'), class: 'header-link' )), cart_path(current_user.cart.id)%>
<span class="cart-quantity" id="cart_quantity"><%= render partial: 'carts/cart_quantity' %></span>
</span>
</div>
<% end %>
</div>
<div class="languages">
<div><%= link_to_unless_current 'UK', url_for(locale: :uk), class: 'header-link' %></div>
<div class="vertical-rod"><span> | </span></div>
<div><%= link_to_unless_current 'EN', url_for(locale: :en), class: 'header-link' %></div>
</div>
</div>
</div>
app/views/carts/_cart_quantity.html.erb
<% @cart_quantity = current_user.nil? ? '' : current_user.cart.cart_items.count %>
<%= @cart_quantity %>
app/views/layouts/application.html.erb
<!DOCTYPE html>
<html>
<head>
<title>Veselka</title>
<meta name="viewport" content="width=device-width,initial-scale=1">
<%= csrf_meta_tags %>
<%= csp_meta_tag %>
<%= stylesheet_link_tag "application", media: "all", "data-turbo-track": "reload" %>
<%= javascript_importmap_tags %>
<%= javascript_include_tag 'turbo', defer: true, "data-turbo-track": "reload" %>
<%= javascript_include_tag 'products' %>
</head>
<body>
<%= render 'shared/header' %>
<div class="start-block container">
<div class="header-msg-container">
<% if flash.any? %>
<%= render 'shared/messages' %>
<% else %>
<div class="breadcrumbs"><%= render_breadcrumbs :separator => ' / ' %></div>
<% end %>
<% render 'shared/messages' %>
</div>
<div class="content-container">
<div class="gap-3"></div>
<div class="sidebar-container">
<div class="logo">
<%= link_to root_path do %>
<%= image_tag('veselka_logo.jpg', class: 'logo-main') %>
<% end %>
</div>
<div class="category" id="categories"><%= link_to t('.categories'), product_categories_path %></div>
<% ProductCategory.all.order(name: :asc).map do |product_category| %>
<div class="category"><%= link_to "#{product_category.name}", product_category_path(product_category, product_category_id: product_category.id) %></div>
<% end %>
</div>
<div class="yield-container">
<%= yield %>
</div>
<div class="gap-3"></div>
</div>
</div>
<%= render 'shared/footer' %>
</body>
</html>
I found the mistake. I tried to update instance variable @cart_quantity in the controller (
turbo_stream.update(@cart_quantity, partial: 'shared/header', locals: {cart_quantity: cart_quantity}),), but sould be updated ID. So resulting line of code must be like this:turbo_stream.replace('cart', partial: 'shared/header', locals: {cart_quantity: cart_quantity}),. The ID 'cart' should be updated. My foul.