Adding New TodosLink to heading


Now that we've seen how LiveComponent works with Hotwire, let's expand the capabilities of our app so we can add new items to the todo list.

Adding a List ComponentLink to heading

In order for our app to support multiple users, we need the ability to manage multiple todo lists. Let's create a TodoListComponent that renders a title and the list of items from a TodoList model. We'll also add a form with a single text field and a submit button.

Collapse file tree Expand file tree
  • app
    • components
      • todo_list_component.html.erb
      • todo_list_component.rb
    • controllers
      • todo_lists_controller.rb
    • models
      • todo_list.rb
    • views
      • todo_lists
        • show.html.erb
  • config
    • routes.rb
1
<h1><%= @todo_list.name %></h1>
2
<ul>
3
  <% todo_items.each do |todo_item| %>
4
    <li><%= todo_item %></li>
5
  <% end %>
6
</ul>
7
<%= form_with(
8
  model: [@todo_list, @todo_list.todo_items.build],
9
  rerender: :self
10
) do |f| %>
11
  <%= f.text_field :text %>
12
  <%= f.submit "Add todo item" %>
13
<% end %>
1
<h1><%= @todo_list.name %></h1>
2
<ul>
3
  <% todo_items.each do |todo_item| %>
4
    <li><%= todo_item %></li>
5
  <% end %>
6
</ul>
7
<%= form_with(
8
  model: [@todo_list, @todo_list.todo_items.build],
9
  rerender: :self
10
) do |f| %>
11
  <%= f.text_field :text %>
12
  <%= f.submit "Add todo item" %>
13
<% end %>

The create ActionLink to heading

As we saw earlier, Turbo automatically handles submitting forms without refreshing the page. Since we passed the rerender argument to form_with, all the state necessary to rerender our TodoListComponent will be submitted along with the form, including the state to rerender all the TodoItemComponent slots. To add the new item, all we have to do is add another slot.

To do so, let's add a basic create action to app/controllers/todo_items_controller (note that we've excluded the update action we wrote earlier for brevity).

class TodoItemsController < ApplicationController
  def create
    @todo_list = TodoList.find(params[:todo_list_id])
    @todo_item = @todo_list.todo_items.create(todo_item_params)
  end

  private

  def todo_item_params
    params.require(:todo_item).permit(:text)
  end
end
class TodoItemsController < ApplicationController
  def create
    @todo_list = TodoList.find(params[:todo_list_id])
    @todo_item = @todo_list.todo_items.create(todo_item_params)
  end

  private

  def todo_item_params
    params.require(:todo_item).permit(:text)
  end
end

Let's create the corresponding template in app/views/todo_items/create.turbo_stream.erb:

<%= live.rerender(todo_list: @todo_list) do |todo_list_component| %>
  <% todo_list_component.with_todo_item(todo_item: @todo_item) %>
<% end %>
<%= live.rerender(todo_list: @todo_list) do |todo_list_component| %>
  <% todo_list_component.with_todo_item(todo_item: @todo_item) %>
<% end %>

When the submit button is clicked, the todo list is rerendered with the new item.