しかも、手動でGameクラスにLineupクラスを複数追加すると、下図のようにリストボックスが複数表示されてしまって見栄えが悪い。
下記のサイトで同じ課題に取り組んでいる人がいたので、参考にさせていただいた。
Complex Forms for Many-to-Many Relationships
http://beacon.wharton.upenn.edu/learning/2012/02/complex-forms-for-many-to-many-relationships/
まずは改良版のGameクラスとLineupクラスのscaffold。(実行結果は省略。)
rails g scaffold bettergame date:date rails g scaffold betterlineup bettergame:references player:references rake db:migrate次にbetterゲームクラスのmodelを下記のように修正する。
# encoding: utf-8
class Bettergame < ActiveRecord::Base
has_many :players, :through => :betterlineups
has_many :betterlineups
accepts_nested_attributes_for :betterlineups
def player_names
players.collect{|p| p.name}.join(', ')
end
# PlayerクラスのIDを配列でget/setできる属性を追加
def player_ids
@player_ids || players.collect{|p| p.id}
end
def player_ids=(id_array)
@player_ids = id_array.collect{|id| id.to_i};
end
after_save :assign_players
private
# PlayerクラスのIDの配列から、Betterlineupクラスを追加/編集/削除する。
def assign_players
if @player_ids
new_ids = @player_ids
old_ids = players.collect{|p| p.id}
ids_to_delete = old_ids - (old_ids & new_ids)
ids_to_add = new_ids - (old_ids & new_ids)
bettergame_id = id
ids_to_delete.each do |player_id|
Betterlineup.destroy_all(:bettergame_id => bettergame_id, :player_id => player_id)
end
ids_to_add.each do |player_id|
Betterlineup.create(:bettergame_id => bettergame_id, :player_id => player_id)
end
end
end
end
リストボックスから複数送信されるPlayerのIDを配列としてまとめて扱うためのメソッドと、送信されたPlayerのIDとLineupクラスで既に保持しているPlayerのIDを比較して、追加/更新/削除を行うメソッドの2つが追加されている。次に、viewの_form.html.erbを下記のように修正する。
<%= form_for(@bettergame) do |f| %>
<% if @bettergame.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(@bettergame.errors.count, "error") %> prohibited this bettergame from being saved:</h2>
<ul>
<% @bettergame.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="field">
<%= f.label :date %><br />
<%= f.date_select :date, :use_month_numbers => true %>
</div>
<div class="field">
<%= f.label :lineups %><br />
<%= f.select(:player_ids, @player_select_data, {}, {:multiple => true, :size => Player.all.size}) %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
セレクトボックスのヘルパーには、先ほど modelクラスに追加した「:player_ids」を指定している。
その次の「@player_select_data」は、リストボックスに表示する値の配列となる。後で Controllerに追加する。
複数選択できるように、「:multiple => true」として、リストボックスの表示サイズは、Player全員とするために、「:size => Player.all.size」としている。最後に、Controllerの修正は下記の通りとなる。(new~update部分のみ)
# GET /bettergames/new
# GET /bettergames/new.json
def new
@bettergame = Bettergame.new
@player_select_data = Player.all.collect{|p| [p.name, p.id]}
respond_to do |format|
format.html # new.html.erb
format.json { render json: @bettergame }
end
end
# GET /bettergames/1/edit
def edit
@bettergame = Bettergame.find(params[:id])
@player_select_data = Player.all.collect{|p| [p.name, p.id]}
end
# POST /bettergames
# POST /bettergames.json
def create
@bettergame = Bettergame.new(params[:bettergame])
respond_to do |format|
if @bettergame.save
format.html { redirect_to @bettergame, notice: 'Bettergame was successfully created.' }
format.json { render json: @bettergame, status: :created, location: @bettergame }
else
format.html { render action: "new" }
format.json { render json: @bettergame.errors, status: :unprocessable_entity }
end
end
end
# PUT /bettergames/1
# PUT /bettergames/1.json
def update
@bettergame = Bettergame.find(params[:id])
respond_to do |format|
if @bettergame.update_attributes(params[:bettergame])
format.html { redirect_to @bettergame, notice: 'Bettergame was successfully updated.' }
format.json { head :no_content }
else
format.html { render action: "edit" }
format.json { render json: @bettergame.errors, status: :unprocessable_entity }
end
end
end
修正箇所は、newとeditのメソッド内で、「@player_select_data」の設定(Playerクラスの全データ取得)を行なっているのみ。これで、複数選択の追加/編集/削除がリストボックス上から行えるようになった。


0 件のコメント:
コメントを投稿