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

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

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


## 準備編

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

``` console
$ 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](https://qiita-image-store.s3.amazonaws.com/0/8607/1c3aedfd-5867-a91a-fbb0-a8b653df07d3.png "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](https://github.com/jacoyutorius/form_with_index) にありますので、もし良ければ参考にしてみてください。

![FormWithIndex.png](https://qiita-image-store.s3.amazonaws.com/0/8607/ec5622b3-0679-dabb-3ef4-9a94482aec6c.png "FormWithIndex.png")