You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Rails分页点击后缺失head标签,CSS/JS未渲染问题求助

问题:Rails视图切换后分页仅渲染局部视图缺失布局

我的Rails应用包含看板视图(kanban view)和经典视图(classic view)两类视图,首次加载经典视图时一切正常。但点击分页(尝试过kaminari和pagy gem)的第2/3页后,页面仅渲染_classic_view.html.erb局部视图,未加载包含所有CSS、JS文件的application.html.erb布局文件,导致浏览器中缺失head标签,页面内容显示异常。


相关代码

index.html.erb

...
<div id="partial-container"></div>
...
<script>
  const partialContainer = document.getElementById('partial-container');
  const valueFromBrowserData = localStorage.getItem('view'); // 返回 kanban 或 classic

  if (valueFromBrowserData) {
    selectDropdown.value = valueFromBrowserData;
    loadPartial(window.location.pathname, valueFromBrowserData, partialContainer);
  }

  form.addEventListener('change', (event) => {
    const dropdownValue = event.target.value;
    localStorage.setItem('view', dropdownValue);
    loadPartial(window.location.pathname, dropdownValue, partialContainer);
  });

  function loadPartial(url, value, container) {
    loaded_partials = true;
    // 显示加载器
    var loader = document.getElementById('loader-style');
    if (loader) {
      loader.style.display = 'flex';
    }
    // 清空容器
    container.innerHTML = '';
    const urlWithParams = `${url}?name=${encodeURIComponent(value)}`
      fetch(urlWithParams)
      .then((response) => response.text())
      .then((partialHtml) => {
        container.innerHTML = partialHtml;
        removeEmptyHeaders();
        // 隐藏加载器
        if (loader) {
          loader.style.display = 'none';
        }
      })
      .catch((error) => {
        console.error('Error loading partial:', error);
        // 出错时隐藏加载器
        if (loader) {
          loader.style.display = 'none';
        }
      });
   }
</script>

_classic_view.html.erb

<div id="flash-notification"></div>
<div class="can-table-data">
<% @data.each do |data|%>
... <!-- 展示数据详情 -->
<% end %>
</div>

<div class="table-pagination">
  <%= paginate @data %>
  <div class="dynamic-page-selection mt-1">
    <div class="dropdown">
      <button class="btn btn-row dropdown-toggle" type="button" data-bs-toggle="dropdown" aria-expanded="false">
        <%= @data.limit_value %> Rows / Page
      </button>
      <ul class="dropdown-menu">
        <% PAGE_SIZES.each do |size| %>
          <li><%= link_to size, url_for(per_page: size), class: "dropdown-item" %></li>
        <% end %>
      </ul>
    </div>
  </div>
</div>

application.html.erb

<!DOCTYPE html>
<html>
  <head>
    <title>Test</title>
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <%= csrf_meta_tags %>
    <%= csp_meta_tag %>
    <!-- Dropzone Stand alone V6 css -->
    <link href="https://unpkg.com/dropzone@6.0.0-beta.1/dist/dropzone.css" rel="stylesheet" type="text/css" />
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/flatpickr/dist/flatpickr.min.css">
    <%= javascript_include_tag "jquery" %>
    <%= stylesheet_link_tag "application.bootstrap", "data-turbo-track": "reload" %>
    <%= javascript_importmap_tags %>
    <%= javascript_include_tag "delete_confirmation_dialog" %>
    <%= javascript_include_tag "polling" %>
    <%= javascript_include_tag 'sortable_developer_fields' %>
    <script type="text/javascript" src="https://www.gstatic.com/charts/loader.js"></script>
  </head>
  <body>
    <%if @current_user %>
      <div class="wrapper">
        <%= render 'layouts/header' %>
        <div class="content-page">
          <div class="custom-container">
            <%= yield %>
          </div>
        </div>
      </div>
    <% else %>
      <%= render 'layouts/flash' %>
      <%= yield %>
    <% end %>
  </body>
</html>

解决方案

1. 控制器区分AJAX与普通请求

在对应控制器的index动作中,判断请求类型,AJAX请求返回局部视图,普通请求返回完整布局:

def index
  @data = YourModel.page(params[:page]).per(params[:per_page] || 20)
  @view = params[:name] || 'classic'

  if request.xhr?
    # AJAX请求(fetch发送)仅渲染局部视图
    render partial: "_#{@view}_view", layout: false
  else
    # 首次加载或整页跳转,渲染完整布局
    render
  end
end

2. 拦截分页链接点击,用AJAX加载

在index.html.erb的script中添加事件监听,阻止分页链接的默认跳转,调用loadPartial加载新页面:

// 监听分页及每页条数选择的点击事件
document.addEventListener('click', function(e) {
  const targetLink = e.target.closest('.pagination a, .dynamic-page-selection a');
  if (targetLink) {
    e.preventDefault();
    const currentView = localStorage.getItem('view') || 'classic';
    loadPartial(targetLink.href, currentView, partialContainer);
  }
});

3. 修正loadPartial的URL参数处理

原有拼接方式会丢失分页参数,改为保留原有URL参数并添加视图参数:

function loadPartial(url, value, container) {
  loaded_partials = true;
  const loader = document.getElementById('loader-style');
  if (loader) loader.style.display = 'flex';
  container.innerHTML = '';
  
  // 解析URL并添加视图参数
  const urlObj = new URL(url);
  urlObj.searchParams.set('name', encodeURIComponent(value));
  
  fetch(urlObj.toString())
    .then(response => response.text())
    .then(partialHtml => {
      container.innerHTML = partialHtml;
      removeEmptyHeaders();
      if (loader) loader.style.display = 'none';
    })
    .catch(error => {
      console.error('Error loading partial:', error);
      if (loader) loader.style.display = 'none';
    });
}

4. 验证逻辑

  • 首次加载是普通HTTP请求,控制器返回完整布局,页面正常渲染。
  • 切换视图或点击分页时,通过AJAX请求获取局部视图,插入到容器中,不会触发整页跳转,保留原有布局的CSS/JS。

内容的提问来源于stack exchange,提问作者hardik

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.05 03:40:01