2

我正在尝试为音乐家创建一个设置列表应用程序。用户可以上传他们的歌曲曲目并从这些歌曲列表中挑选歌曲以填充特定表演的设置列表。

见图片: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" )

这是对 setlists 控制器的 create 操作的发布请求(包括在新操作中创建的歌曲的排列 id 和 Setlist.new 的 id 的参数),如下所示:

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  

理想情况下,我希望在每次单击添加/箭头按钮后(远程)为同一个设置列表创建一个新项目,直到用户完成创建该设置列表。为了在右侧显示,我有:

<% 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 参数正确传递的。

此外,每次刷新页面时,都会根据我在新操作中的内容生成一个新的集合列表,因此添加的每个项目都会进入一个集合列表,然后生成一个空白集合列表,因此右侧不会出现任何内容.

最后,这看起来很混乱。有人有更好的设计策略来实现这一点吗?任何帮助将不胜感激!!!

4

1 回答 1

0

这里的主要问题是您的控制器违反了约定。

SetlistController应该Setlist在用户发布到/setlists.

如果您想创建一个新的子记录,您将 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
    }
  ]
} 

真正的好处是用户可以执行多个操作,例如添加/删除,您只需将单个 setlist 推送到数据库以更新状态,而不是对每个子记录使用多个 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 的一部分提供。

于 2015-11-11T22:50:45.270 回答