しかも、手動で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 件のコメント:
コメントを投稿