Ruby on rail. I want to show all todo-lists with their associated todo-items. How I can do that?

Viewed 61

How I can show at the same time on view data from two tables: first has type has_many and second has type belongs_to I don`t understand how to display data from main page list and all related strings from table list_items. Help me, please.

Controllers of list and list_item

class TodoListsController < ApplicationController
  before_action :set_todo_list, only: [:show, :edit, :update, :destroy]

  def index
    @todo_lists = TodoList.all
  end

  def show
  end

  def new
    @todo_list = TodoList.new
  end

  def edit
  end

  def create
    @todo_list = TodoList.new(todo_list_params)

    respond_to do |format|
      if @todo_list.save
        format.html { redirect_to @todo_list, notice: 'Todo list was successfully created.' }
        format.json { render :show, status: :created, location: @todo_list }
      else
        format.html { render :new }
        format.json { render json: @todo_list.errors, status: :unprocessable_entity }
      end
    end
  end

  def update
    respond_to do |format|
      if @todo_list.update(todo_list_params)
        format.html { redirect_to @todo_list, notice: 'Todo list was successfully updated.' }
        format.json { render :show, status: :ok, location: @todo_list }
      else
        format.html { render :edit }
        format.json { render json: @todo_list.errors, status: :unprocessable_entity }
      end
    end
  end

  def destroy
    @todo_list.destroy
    respond_to do |format|
      format.html { redirect_to root_url, notice: 'Todo list was successfully destroyed.' }
      format.json { head :no_content }
    end
  end

  private
    def set_todo_list
      @todo_list = TodoList.find(params[:id])
    end

    def todo_list_params
      params.require(:todo_list).permit(:title, :description)
    end
end
class TodoItemsController < ApplicationController
    before_action :set_todo_list
    before_action :set_todo_item, except:[:create]

    def create
        @todo_item = @todo_list.todo_items.create(params[:todo_item].permit(:content))
        redirect_to @todo_list
    end

    def destroy
        if @todo_item.destroy
            flash[:success] = "Todo List item was deleted."
        else
            flash[:error] = "Todo List item could not be deleted."
        end
        redirect_to @todo_list
    end

    def complete
        @todo_item.update_attribute(:completed_at, Time.now)
        redirect_to @todo_list, notice: "Todo item completed"
        
    end

    private


    def set_todo_list
        @todo_list = TodoList.find(params[:todo_list_id])
    end

    def set_todo_item
        @todo_item = @todo_list.todo_items.find(params[:id])
    end


    def todo_item_params
        params[:todo_item].permit(:content)
    end
end

I have such models

class TodoList < ApplicationRecord
    has_many :todo_items, dependent: :delete_all
end
class TodoItem < ApplicationRecord
  belongs_to :todo_list

  def completed?
    !completed_at.blank?
    
  end
end

and have such migrations

class CreateTodoLists < ActiveRecord::Migration[6.0]
  def change
    create_table :todo_lists do |t|
      t.string :title
      t.text :description

      t.timestamps
    end
  end
end
class CreateTodoItems < ActiveRecord::Migration[6.0]
  def change
    create_table :todo_items do |t|
      t.string :content
      t.references :todo_list, foreign_key: true
      # t.references :todo_list, null: false, foreign_key: true

      t.timestamps
    end
  end
end
1 Answers
class TodoLists < ApplicationController
  ...

  # GET /todo_lists
  def index
    @todo_lists = TodoList.include(:todo_items).all
  end
end

Using .includes prevents a N+1 query as all items for all the lists will be loaded in one query.

You then just iterate through the lists and loop through the items for each list.

Lets start by creating a partial to render a todo_list:

# app/views/todo_lists/_todo_list.html.erb
<article>
  <h1><%= todo_list.title %></h1>
  <p><%= todo_list.description %></p>
  <% if todo_list.todo_items.any? %>
    <ul>
    <% todo_list.todo_items.each do |item| %>
      <li><%= item.content %></li>
    <% end %>
    </ul>
  <% else %>
    <p>This list contains no items.</p>
  <% end %>
</article>

Then lets just render our partial with the collection from the index view:

# app/views/todo_lists/index.html.erb
<%= render(@todo_lists) || content_tag("p", "There are no todo lists.") %>
Related