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

Rails5:如何在index.html.erb中同时展示待办清单及其子项?

Hey there! Let's break down the best practices for displaying your todo lists and their associated items on the index.html.erb page—this is a super common pattern, and we can make it clean, performant, and user-friendly with a few key steps:

1. Fix Backend Data Loading First (Avoid N+1 Queries)

Before touching the view, make sure you're not hitting the database multiple times for each list's items. This is called an N+1 query problem, and it'll slow down your page as your dataset grows.

In your TodoListsController's index action, use includes to preload all associated todo items in a single database call:

def index
  # Preloads all todo_items for every todo_list in one go
  @todo_lists = TodoList.includes(:todo_items).all
end

This ensures you only make two database queries total (one for lists, one for all their items) instead of 1 + number_of_lists queries.

2. Build a Semantic, Maintainable View Structure

Use nested HTML elements to clearly separate each list from its items, and add classes for styling and interactivity. Here's a clean example:

<div class="todo-lists-container">
  <% @todo_lists.each do |todo_list| %>
    <section class="todo-list">
      <h2 class="todo-list__title"><%= todo_list.name %></h2>
      
      <ul class="todo-list__items">
        <% todo_list.todo_items.each do |item| %>
          <li class="todo-item <%= 'todo-item--completed' if item.completed? %>">
            <%= check_box_tag "todo_item[#{item.id}]", item.id, item.completed, 
                remote: true, data: { item_id: item.id } %>
            <span class="todo-item__text"><%= item.description %></span>
          </li>
        <% end %>
      </ul>
    </section>
  <% end %>
</div>
  • Semantic tags like <section> and <ul> make your markup accessible and easy to style.
  • The todo-item--completed class lets you visually distinguish finished items (e.g., strikethrough text).
  • We added remote: true to the checkbox for AJAX-powered updates (more on that next).
3. Add Smooth Interactive Checkbox Updates

Instead of reloading the whole page when a user checks an item, use AJAX to update the state asynchronously. Here's how to set it up:

First, add a route for updating todo items

In config/routes.rb:

resources :todo_items, only: [:update]

Then, handle the update in your controller

class TodoItemsController < ApplicationController
  def update
    @todo_item = TodoItem.find(params[:id])
    # Toggle the completed state based on whether the checkbox is checked
    @todo_item.update(completed: params[:todo_item][@todo_item.id].present?)
    
    respond_to do |format|
      format.js # This will render app/views/todo_items/update.js.erb
    end
  end
end

Finally, add a JS response to update the DOM

Create app/views/todo_items/update.js.erb:

const todoItem = document.querySelector(`.todo-item[data-item-id="<%= @todo_item.id %>"]`);

// Toggle the completed class based on the new state
if (<%= @todo_item.completed? %>) {
  todoItem.classList.add('todo-item--completed');
} else {
  todoItem.classList.remove('todo-item--completed');
}

This will instantly update the item's appearance without a page refresh.

4. Optional: Add Collapsible Lists for Better UX

If you have long lists of items, let users collapse/expand lists to keep the page tidy. Add a simple click handler to the list title:

Update the view to include a toggle icon

<h2 class="todo-list__title" data-list-id="<%= todo_list.id %>">
  <%= todo_list.name %>
  <span class="todo-list__toggle">▼</span>
</h2>

Add CSS for hiding items

.todo-list__items--hidden {
  display: none;
}

Add a small JS snippet for toggling

document.addEventListener('DOMContentLoaded', () => {
  document.querySelectorAll('.todo-list__title').forEach(title => {
    title.addEventListener('click', () => {
      const listId = title.dataset.listId;
      const items = document.querySelector(`.todo-list[data-list-id="${listId}"] .todo-list__items`);
      const toggleIcon = title.querySelector('.todo-list__toggle');
      
      items.classList.toggle('todo-list__items--hidden');
      toggleIcon.textContent = items.classList.contains('todo-list__items--hidden') ? '▶' : '▼';
    });
  });
});
5. For Large Datasets: Add Pagination or Lazy Loading

If you expect hundreds of lists/items, don't load everything at once. Use pagination for lists (via the will_paginate or kaminari gems) or lazy load items for each list (using Turbo Frames if you're on Rails 7+).

For example, show the first 5 items per list and let users load more:

<ul class="todo-list__items">
  <% todo_list.todo_items.limit(5).each do |item| %>
    <!-- Render first 5 items -->
  <% end %>
  <% if todo_list.todo_items.count > 5 %>
    <%= turbo_frame_tag "more-items-#{todo_list.id}" do %>
      <%= link_to "View all #{todo_list.todo_items.count} items", more_todo_list_items_path(todo_list), remote: true %>
    <% end %>
  <% end %>
</ul>

These steps cover the core best practices: optimizing data loading, building clean markup, adding smooth interactions, and scaling for larger datasets.

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.20 12:05:24