Rails:如何在一个视图中将项目增量添加到排序列表中
Rails: How can I incrementally add items to a sorted list in one view
我正在尝试为音乐家创建一个设置列表应用程序。用户可以上传他们的歌曲曲目并从这些歌曲列表中挑选歌曲,以填充特定表演的曲目列表。
见图:setlist_screenshot。
单击左侧歌曲上的箭头图标应将歌曲添加到右侧的sortable列表中。
有问题的模型:
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
基本上一个歌单有很多编曲(歌曲)通过项目(加入table),编曲(歌曲)当然可以在很多歌单上。
我在视图中添加歌曲按钮的代码是:
<%= link_to(organization_setlists_path(:arrangement_id => song.arrangements.first.id, setlist_id: @new_set.id, remote: true), method: "post", class: "secondary-content" )
这是对 setlists 控制器的创建操作的 post 请求(包括歌曲编曲 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
理想情况下,我希望在每次单击 add/arrow 按钮后(远程)为同一设置列表创建一个新项目,直到用户完成该设置列表的创建。要在右侧显示它,我有:
<% 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 参数。
此外,每次刷新页面时,都会根据我在新操作中的内容生成一个新的设置列表,因此添加的每个项目都会进入一个设置列表,然后生成一个空白的设置列表,所以什么都不会永远出现在右边。
最后,这看起来很乱。任何人都有更好的设计策略来实现这一目标?任何帮助将不胜感激!!!
这里的主要问题是您的控制器违反了约定。
当用户 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
}
]
}
真正的好处是用户可以执行多个操作,例如adding/removing,你只需要将单个 setlist 推送到数据库来更新状态,而不是使用多个 POST / 删除每个子记录的调用。
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 功能和拖放重新排序等有点超出范围。
请注意,这些解决方案不是唯一的。您可以轻松地在您的应用程序中同时拥有这两个选项(嵌套属性和指定的控制器),具体取决于您的要求是否也需要您能够单独操作子记录,或者您是否希望将其作为 [=52= 的一部分提供].
我正在尝试为音乐家创建一个设置列表应用程序。用户可以上传他们的歌曲曲目并从这些歌曲列表中挑选歌曲,以填充特定表演的曲目列表。
见图:setlist_screenshot。 单击左侧歌曲上的箭头图标应将歌曲添加到右侧的sortable列表中。
有问题的模型:
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
基本上一个歌单有很多编曲(歌曲)通过项目(加入table),编曲(歌曲)当然可以在很多歌单上。
我在视图中添加歌曲按钮的代码是:
<%= link_to(organization_setlists_path(:arrangement_id => song.arrangements.first.id, setlist_id: @new_set.id, remote: true), method: "post", class: "secondary-content" )
这是对 setlists 控制器的创建操作的 post 请求(包括歌曲编曲 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
理想情况下,我希望在每次单击 add/arrow 按钮后(远程)为同一设置列表创建一个新项目,直到用户完成该设置列表的创建。要在右侧显示它,我有:
<% 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 参数。
此外,每次刷新页面时,都会根据我在新操作中的内容生成一个新的设置列表,因此添加的每个项目都会进入一个设置列表,然后生成一个空白的设置列表,所以什么都不会永远出现在右边。
最后,这看起来很乱。任何人都有更好的设计策略来实现这一目标?任何帮助将不胜感激!!!
这里的主要问题是您的控制器违反了约定。
当用户 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
}
]
}
真正的好处是用户可以执行多个操作,例如adding/removing,你只需要将单个 setlist 推送到数据库来更新状态,而不是使用多个 POST / 删除每个子记录的调用。
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 功能和拖放重新排序等有点超出范围。
请注意,这些解决方案不是唯一的。您可以轻松地在您的应用程序中同时拥有这两个选项(嵌套属性和指定的控制器),具体取决于您的要求是否也需要您能够单独操作子记录,或者您是否希望将其作为 [=52= 的一部分提供].