RailsでScaffoldで画面を作ると、丁寧に新規・修正毎にViewができあがります。
ですが、できれば同じ1つの画面内で新規も修正もやりたいと思ったこと、ありませんか?
ということでindexページ内で全て新規登録も修正も完結するサンプルを作ってみました。
準備編
まずscaffoldでCRUD画面をちゃっちゃっと作りますね。
$ rails g scaffold User name:string age:decimal note:text
適当にテストデータを入れます。
Hashieとか使っているのは趣味です。
[
{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ページをルートに設定しておきます。
Rails.application.routes.draw do
root "users#index"
resources :users
end
新規登録編
_form.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オブジェクトを作成します。
def index
@users = User.all
@user = User.new #追記
end
こんな見た目になります。
OK。表示はできました。
この状態でも更新できますが、users/:id/showへ遷移してしまうので更新後に元のindexページに戻るようにします。
redirect_to users_path
(今回はrootがusers#indexなので、root_pathでも同じですが。)
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"をクリックしたときの遷移先も直しておきます。
<%= link_to 'New User', users_path %>
この状態でformからレコードを登録すると、パッと見はページ遷移ナシでリストにレコードが追加されたように見えます。
更新編
さて、これでindexページにformを追加して新規レコード作成ができるようにしてみましたが、
今度は編集まで行えるようにしてみます。
まずは、「edit」リンクを踏んだ先の遷移先を、/users/:id/editページではなく、同じくindexページのままで、formに編集対象のレコードの値が表示されるようにします。
更新対象のレコードを取得するため、users_pathにレコードのidを渡すようにします。
<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メソッドが存在するのでそのまま使っています。
def index
@users = User.all
if params[:id].present?
set_user
else
@user = User.new
end
end
更新後には選択していたレコードの内容をそのままformに表示させておきたいので、
update後のリダイレクト先はusers_pathではなく、request.refererとしておきます。
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からも消しておいたほうが良いですね。
resources :users, expect: [:show, :new, :edit]
そんなこんなで完成です。
作ったサンプルは
https://github.com/jacoyutorius/form_with_index にありますので、もし良ければ参考にしてみてください。