Railsで更新も編集も削除も、全部indexページでやりたい

  • 35
    いいね
  • 1
    コメント
この記事は最終更新日から1年以上が経過しています。

RailsでScaffoldで画面を作ると、丁寧に新規・修正毎にViewができあがります。
ですが、できれば同じ1つの画面内で新規も修正もやりたいと思ったこと、ありませんか?

ということでindexページ内で全て新規登録も修正も完結するサンプルを作ってみました。

準備編

まずscaffoldでCRUD画面をちゃっちゃっと作りますね。

$ rails g scaffold User name:string age:decimal note:text

適当にテストデータを入れます。
Hashieとか使っているのは趣味です。

seed.rb
[
    {name: "Spike Spiegel", age: 27, birth: "2044-06-26", note: "SwordFish2"},
    {name: "Jet Black", age: 36, birth: "2035-12-03", note: "Hammerhead"},
    {name: "Faye Valentine", age: 77, note: "1994-08-14", note: "redtail"},
    {name: "Edward Wong Hau Pepelu Tivrusly 4", age: 13, birth: "2058-01-01", note: ""},
    {name: "Ein", age: 2}
].map{|row| Hashie::Mash.new(row) }.each do |data|
    User.create!(
        name: data.name,
        age: data.age,
        birth: data.birth,
        note: data.note
        )
end

indexページをルートに設定しておきます。

config/routes.rb
Rails.application.routes.draw do
  root "users#index"
  resources :users
end

新規登録編

_form.html.erbの中身をまずはガバっと貼り付けます。

users/index.html.erb

# -------------ここから----------------
<h1>Listing users</h1>

<%= form_for(@user) do |f| %>
  <% if @user.errors.any? %>
    <div id="error_explanation">
      <h2><%= pluralize(@user.errors.count, "error") %> prohibited this user from being saved:</h2>

      <ul>
      <% @user.errors.full_messages.each do |message| %>
        <li><%= message %></li>
      <% end %>
      </ul>
    </div>
  <% end %>

  <div class="field">
    <%= f.label :name %><br>
    <%= f.text_field :name %>
  </div>
  <div class="field">
    <%= f.label :age %><br>
    <%= f.text_field :age %>
  </div>
  <div class="field">
    <%= f.label :note %><br>
    <%= f.text_area :note %>
  </div>
  <div class="actions">
    <%= f.submit %>
  </div>
<% end %>

# -------------ここまで----------------


<table>
  <thead>
    <tr>
      <th>Name</th>
      <th>Age</th>
      <th>Note</th>
      <th colspan="3"></th>
    </tr>
  </thead>

  <tbody>
    <% @users.each do |user| %>
      <tr>
        <td><%= user.name %></td>
        <td><%= user.age %></td>
        <td><%= user.note %></td>
        <td><%= link_to 'Show', user %></td>
        <td><%= link_to 'Edit', edit_user_path(user) %></td>
        <td><%= link_to 'Destroy', user, method: :delete, data: { confirm: 'Are you sure?' } %></td>
      </tr>
    <% end %>
  </tbody>
</table>

<br>

<%= link_to 'New User', new_user_path %>

コントローラでform_forヘルパに渡すための@userオブジェクトを作成します。

app/controllers/users_controller.rb
def index
  @users = User.all
  @user = User.new   #追記
end

こんな見た目になります。

FormWithIndex.png

OK。表示はできました。
この状態でも更新できますが、users/:id/showへ遷移してしまうので更新後に元のindexページに戻るようにします。

redirect_to users_path

(今回はrootがusers#indexなので、root_pathでも同じですが。)

app/controllers/users_controller.rb
def create
  @user = User.new(user_params)

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

"New User"をクリックしたときの遷移先も直しておきます。

_form.html.erb
<%= link_to 'New User', users_path %>

この状態でformからレコードを登録すると、パッと見はページ遷移ナシでリストにレコードが追加されたように見えます。

更新編

さて、これでindexページにformを追加して新規レコード作成ができるようにしてみましたが、
今度は編集まで行えるようにしてみます。

まずは、「edit」リンクを踏んだ先の遷移先を、/users/:id/editページではなく、同じくindexページのままで、formに編集対象のレコードの値が表示されるようにします。

更新対象のレコードを取得するため、users_pathにレコードのidを渡すようにします。

_form.html.erb
<td><%= link_to 'Edit', edit_user_path(user) %></td><td><%= link_to 'Edit', users_path(id: user.id) %></td>

次はコントローラ側です。
paramsにidが含まれている場合は、そのIDから検索したUserオブジェクトを返すようにします。
User.findとやってもいいのですが、既にscaffoldで作成されたset_userメソッドが存在するのでそのまま使っています。

app/controllers/users_controller.rb
def index
  @users = User.all

  if params[:id].present?
    set_user
  else
    @user = User.new
  end
end

更新後には選択していたレコードの内容をそのままformに表示させておきたいので、
update後のリダイレクト先はusers_pathではなく、request.refererとしておきます。

app/controllers/users_controller.rb
def update
  respond_to do |format|
    if @user.update(user_params)
      format.html { redirect_to request.referer, notice: 'User was successfully updated.' }
      format.json { render :show, status: :ok, location: @user }
    else
      format.html { render :edit }
      format.json { render json: @user.errors, status: :unprocessable_entity }
    end
  end
end

削除編

削除は何も変えなくてOKですね。

その他

show/new/editアクションは使わないので、ルーティングから削除してcontrollerからも消しておいたほうが良いですね。

config/routes.rb
resources :users, expect: [:show, :new, :edit]

そんなこんなで完成です。
作ったサンプルは
https://github.com/jacoyutorius/form_with_index にありますので、もし良ければ参考にしてみてください。

FormWithIndex.png