2015-11-11 15 views
2

我正在嘗試爲音樂家創建一個列表應用程序。用戶可以上傳他們的歌曲曲目&從這些歌曲列表中挑選歌曲以填充特定演出的集合列表。Rails:我如何在一個視圖中將項目增量添加到排序列表中

查看圖片:setlist_screenshot。 單擊左側上的歌曲上的箭頭圖標應將該歌曲添加到右側上的可排序列表中。

模式有問題:

class Setlist < ActiveRecord::Base 
    belongs_to :organization 
    # join between Set Lists & Arrangements(songs) 
    has_many :items, -> { order(position: :asc) } 
    has_many :arrangements, through: :items 
end 

-

class Item < ActiveRecord::Base 
    # join between Set Lists & Arrangements(songs) 
    belongs_to :arrangement 
    belongs_to :setlist 
    acts_as_list scope: :setlist 
end 

-

class Arrangement < ActiveRecord::Base 
    belongs_to :song 
    belongs_to :organization 
    has_many :attachments  
    # join between Set Lists & Arrangements(songs) 
    has_many :items 
    has_many :setlists, through: :items 
end 

基本上是一個演出曲目有很多的安排(這首歌)直通項目(連接表),並安排(歌曲)當然可以在許多曲目列表中。

我有用於按鈕添加歌曲的視圖中的代碼是:

<%= link_to(organization_setlists_path(:arrangement_id => song.arrangements.first.id, setlist_id: @new_set.id, remote: true), method: "post", class: "secondary-content") 

其是交請求(包括對PARAMS歌曲的安排ID & ID的Setlist.new在製成新動作)到的setlists控制器的創建操作,如下:

class SetlistsController < ApplicationController 
before_action :authenticate_user! 

def new 
    if @new_set.nil? 
     @new_set = Setlist.create 
     @new_item = Item.new 
    end 
end 

def create 
    @arrangement = Arrangement.find(params[:arrangement_id]) 
    @new_item = Item.new 
    @new_item.setlist_id = Setlist.find(params[:setlist_id]) 
    @new_item.arrangement_id = @arrangement.id 
    if @new_item.save 
     flash[:message] = "Song Sucessfully Added!" 
    else 
     flash[:message] = "Something Went Wrong" 
    end 
end 

理想情況下,我想添加/箭頭按鈕的每次點擊後要創建(遠程)對於相同的setlist一個新項,直到用戶完成創建該集合列表。要顯示在右邊,我有:

<% if @new_set && @new_set.items.count >=1 %> 
    <ul> 
    <% @new_set.items.each do |item| %> 
     <li class="center-align"><%= item.arrangement.title %></li> 
    <% end %> 
    </ul> 
<% else %> 
    <p class="center-align">Add Songs From The List On The Left</p> 
<% end %> 

最終,用戶應該能夠完成它,並繼續創建另一組名單,如果他們想。

我真的不知道如何去做這件事,而不是我在這裏所做的,這是行不通的。問題是:

其中之一,創建操作實際上是創建一個項目&分配新項目的外鍵安排,但不是爲了某種原因的設定列表,即使是集合ID正在從link_to params。另外,每次刷新頁面時,都會根據我在新動作中生成的新生成列表生成新的生成列表,因此每添加一個項目都會進入一個生成列表,然後生成一個空白生成列表,因此不會有任何結果出現在右側。

最後,這似乎只是混亂在一起。任何人都有更好的設計策略來實現這一目標?任何幫助將受到嚴厲的讚賞!

回答

0

這裏的主要問題是您的控制器違反了慣例。

當用戶POST到/setlists時,您的SetlistController應創建一個Setlist

如果要創建新的子記錄,則需要POST到/setlists/:setlist_id/items。這將由ItemsController處理。

# routes.rb 
resources :setlists, shallow: true do 
    resources :items 
end 

class ItemsController < ApplicationController 

    respond_to :html 
    before_action :find_setlist!, only: [:create, :index] 

    def create 
    @item = @setlist.items.build(item_params) 
    @item.save 
    respond_with @item 
    end 

    # ... 

    private 
    def find_setlist! 
     @setlist = Setlist.includes(:items).find!(params[:setlist_id]) 
    end 

    def items_params 
     params.require(:item) 
      .permit(
       :arrangement_id 
      ) 
    end 
end 

<% @arrangements.each do |a| %> 
    <%= button_to 'Add to set', setlist_items_path(@setlist), 
     arrangement_id: a.id, remote: true 
    %> 
<% end %> 

一種替代是使用accepts_nested_attibutes_for :items以允許Setlist接受的項目的參數爲好。

這裏的關鍵區別是增加額外的項目添加到現有的演出曲目也將例如使用時:

PATCH /setlist/:id 
params: { 
    item_attributes: [ 
    { 
     arrangement_id: 2 
    }, 
    { 
     arrangement_id: 3 
    } 
    ] 
} 

真正的好處是,用戶可以進行多種操作,如添加/只刪除你需要將單個集列表推送到數據庫以更新狀態,而不是對每個子記錄使用多個POST/DELETE調用。

class Setlist < ActiveRecord::Base 
    belongs_to :organization 
    # join between Set Lists & Arrangements(songs) 
    has_many :items, -> { order(position: :asc) } 
    has_many :arrangements, through: :items 
    accepts_nested_attributes_for :items, allow_destroy: true 
end 

class SetlistsController < ApplicationController 

    respond_to :html 
    before_action :set_setlist, only: [:show, :edit, :update, :destroy] 

    def new 
    @setlist = Setlist.new 
    @setlist.items.build 
    end 

    def create 
    @setlist = Setlist.new(setlist_params) 
    @setlist.save 
    respond_with @setlist 
    end 

    def update 
    @setlist.update(setlist_params) 
    @setlist.items.build 
    respond_with @setlist 
    end 

    # ... 

    private 
    def set_setlist 
     @setlist = Setlist.includes(:items).find(params[:id]) 
    end 

    def setlist_params 
     params.require(:setlist) 
      .permit(
       :foo, :bar # attributes for the list 
       items_attributes: [ :position, :arrangement_id, :_destroy ] 
      ) 
    end 
end 

的基本形式,建立以支持,這將是這個樣子:

<%= form_for(@setlist) do |f| %> 
    <%= fields_for :items do |item| %> 
    <%= item.number_field :postition %> 
    <%= item.collection_select(:arrangement_id, @setlist.organization.arrangements, :id, :name) %> 
    <%= item.checkbox :_destroy, label: 'Delete?' %> 
    <% end %> 
<% end %> 

當然,這只是一個普通的老同步HTML表單 - 添加AJAX功能並拖放重新排序等是有點超出範圍。

請注意,這些解決方案不是排他性的。根據您的需求是否需要您可以單獨操作子記錄,或者您是否希望將其作爲API的一部分提供,您可以輕鬆地在應用程序中擁有這兩個選項(嵌套屬性和指定的控制器)。

+1

你可能想搜索Github的幾個不同的'Todo'應用程序,並研究他們如何解決這個問題。這是非常相似的情況。 – max

+0

非常感謝!在我發佈這篇文章之後,不久之後,我就指出了濫調setlists控制器的問題,並且您的回答充實了更多需要做的事情。我想我習慣於在新動作中創建和「建立」對象並將它們保存在創建中,而這種情況要求我在編輯動作中執行實際的「構建」,以params中的setlist id添加依賴對象時的引用。如果我的理解正確,在第二種解決方案中,表單將處於編輯視圖中,對嗎?這種形式會產生一個下拉框? –

+0

如果是這樣,我將如何使用該解決方案(使用表單),只需點擊一下按鈕,而不是下拉菜單? –

相關問題