Rails 7.0.4 Turbostream does not refresh partial outside yield-block

121 Views Asked by At

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>
    
1

There are 1 best solutions below

0
Stas Fly On

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.