如何使嵌套表单的输出保持其准确的原始顺序,尽管有排列,使用索引操作和索引模板?
How to get the output of a nested form to stay in its exact original order, despite permutations, using the index action & index template?
我在 post Url 和文本中使用嵌套表单。通过 javascript,我允许对这两个字段进行任何排列,例如Url1 文本 1 文本 2 Url2 或 Url1 文本 1 Url2 文本 2 等
提交表单并重定向到 index.html.view 后,我将尝试保留我决定在表单中使用的任何排列的原始顺序,即如果我决定 post Text1 Url1 Url2 Text2 Url3 我需要在浏览器中显示完全相同的排列。
Post 表单代码
Post 型号
class Post < ActiveRecord::Base
has_many :texts
has_many :urls
accepts_nested_attributes_for :texts, :urls
end
文本模型
class Text < ActiveRecord::Base
belongs_to :post
end
Url 型号
class Url < ActiveRecord::Base
belongs_to :post
end
架构
ActiveRecord::Schema.define(version: 20160215123916) do
create_table "posts", force: :cascade do |t|
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "texts", force: :cascade do |t|
t.text "textattr"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "post_id"
end
add_index "texts", ["post_id"], name: "index_texts_on_post_id"
create_table "urls", force: :cascade do |t|
t.string "urlattr"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "post_id"
end
add_index "urls", ["post_id"], name: "index_urls_on_post_id"
end
Posts 控制器
class PostsController < ApplicationController
def index
@posts = Post.includes(:texts, :urls).all.order(created_at: :desc)
end
def new
@post = Post.new
end
def create
@post = Post.new(post_params)
if @post.save
redirect_to '/posts'
else
render 'new'
end
end
private
def post_params
params.require(:post).permit(:texts_attributes => [:textattr], :urls_attributes => [:urlattr])
end
end
Index.html.erb(尝试失败)
<% @posts.each do |post| %>
<% post.texts.each do |text|%>
<%= text.textattr %> <br>
<% end %>
<% post.urls.each do |url|%>
<%= url.urlattr %> <br>
<% end %>
<% end %>
Index.html.erb(尝试失败)
<% @posts.each do |post| %>
<% [post.texts, post.urls].flatten.sort_by { |c| [c.created_at] }.each do |content| %>
<% if content.class.name == "Text" %>
<%= content.textattr %>
<% elsif content.is_a? Url %>
<%= content.urlattr %>
<% end %>
<% end %>
<% end %>
根据我之前的回答,您肯定需要将 texts
和 urls
对象放入一个 table/model 中。这将允许您为它们分配 "order" 值。
我会向 texts
/ urls
添加一个 order
值,然后使用 child_index
值填充它:
#app/views/posts/new.html.erb
<%= form_for @post do |f| %>
<%= f.fields_for :meta do |t| %>
<%= t.text_field :textattr %>
<%= t.hidden_field :order, t.index %>
<% end %>
<%= f.submit %>
<% end %>
根据文档:
When a collection is used you might want to know the index
of each object into the array. For this purpose, the index
method is available in the FormBuilder
object.
<%= form_for @person do |person_form| %>
...
<%= person_form.fields_for :projects do |project_fields| %>
Project #<%= project_fields.index %>
...
<% end %>
...
<% end %>
--
这将允许您将 order
属性传递给您的数据库:
#app/controllers/posts_controller.rb
class PostsController < ApplicationController
def create
@post = Post.new post_params
@post.save
end
private
def post_params
params.require(:post).permit(meta_attributes: [:order, :textattr])
end
end
更新
我刚试过这种方法,可以确认它有效 (index
):
我唯一要说的是你的模型中可能没有 order
列,尽管它不应该返回合并错误。
我注意到您使用的是过时的 javascript 代码,您可以尝试不使用该代码看看会发生什么吗?
作为 ,您需要添加一个 order
列(或 position
- 调用列 order
会导致与生成的 SQL 混淆).
我认为主要问题是您的 coffeescript code。您需要为添加的每一行生成一个隐藏输入,并为您添加的行使用一个 current_index
计数器:
current_index = 0
addText = ->
html = """
<br>
<textarea name="post[things_attributes][#{current_index}][text]" id="post_things_attributes_#{current_index}_text"></textarea>
<input type="hidden" name="post[things_attributes][#{current_index}][order]" id="post_things_attributes_#{current_index}_order" value="#{current_index}" />
"""
$("#new_post input[type='submit']").before(html)
current_index += 1
addUrl = ->
html = """
<br>
<input type="url" name="post[things_attributes][#{current_index}][url]" id="post_things_attributes_#{current_index}_url">
<input type="hidden" name="post[things_attributes][#{current_index}][order]" id="post_things_attributes_#{current_index}_order" value="#{current_index}" />
"""
$("#new_post input[type='submit']").before(html)
current_index += 1
在您的 index.html.erb
中,按 order
列排序 Things
:
<% @posts.each do |post| %>
<% post.things.order(:order).each do |thing| %>
<br>
<%= thing.try(:text) %>
<%= thing.try(:url) %>
<% end %>
<br>
<% end %>
还值得注意的是,cocoon gem 很好地处理了这种动态嵌套表单模式。我建议您看一下并在您的项目中使用它。
我在 post Url 和文本中使用嵌套表单。通过 javascript,我允许对这两个字段进行任何排列,例如Url1 文本 1 文本 2 Url2 或 Url1 文本 1 Url2 文本 2 等
提交表单并重定向到 index.html.view 后,我将尝试保留我决定在表单中使用的任何排列的原始顺序,即如果我决定 post Text1 Url1 Url2 Text2 Url3 我需要在浏览器中显示完全相同的排列。
Post 表单代码
Post 型号
class Post < ActiveRecord::Base
has_many :texts
has_many :urls
accepts_nested_attributes_for :texts, :urls
end
文本模型
class Text < ActiveRecord::Base
belongs_to :post
end
Url 型号
class Url < ActiveRecord::Base
belongs_to :post
end
架构
ActiveRecord::Schema.define(version: 20160215123916) do
create_table "posts", force: :cascade do |t|
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "texts", force: :cascade do |t|
t.text "textattr"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "post_id"
end
add_index "texts", ["post_id"], name: "index_texts_on_post_id"
create_table "urls", force: :cascade do |t|
t.string "urlattr"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "post_id"
end
add_index "urls", ["post_id"], name: "index_urls_on_post_id"
end
Posts 控制器
class PostsController < ApplicationController
def index
@posts = Post.includes(:texts, :urls).all.order(created_at: :desc)
end
def new
@post = Post.new
end
def create
@post = Post.new(post_params)
if @post.save
redirect_to '/posts'
else
render 'new'
end
end
private
def post_params
params.require(:post).permit(:texts_attributes => [:textattr], :urls_attributes => [:urlattr])
end
end
Index.html.erb(尝试失败)
<% @posts.each do |post| %>
<% post.texts.each do |text|%>
<%= text.textattr %> <br>
<% end %>
<% post.urls.each do |url|%>
<%= url.urlattr %> <br>
<% end %>
<% end %>
Index.html.erb(尝试失败)
<% @posts.each do |post| %>
<% [post.texts, post.urls].flatten.sort_by { |c| [c.created_at] }.each do |content| %>
<% if content.class.name == "Text" %>
<%= content.textattr %>
<% elsif content.is_a? Url %>
<%= content.urlattr %>
<% end %>
<% end %>
<% end %>
根据我之前的回答,您肯定需要将 texts
和 urls
对象放入一个 table/model 中。这将允许您为它们分配 "order" 值。
我会向 texts
/ urls
添加一个 order
值,然后使用 child_index
值填充它:
#app/views/posts/new.html.erb
<%= form_for @post do |f| %>
<%= f.fields_for :meta do |t| %>
<%= t.text_field :textattr %>
<%= t.hidden_field :order, t.index %>
<% end %>
<%= f.submit %>
<% end %>
根据文档:
When a collection is used you might want to know the
index
of each object into the array. For this purpose, theindex
method is available in theFormBuilder
object.<%= form_for @person do |person_form| %> ... <%= person_form.fields_for :projects do |project_fields| %> Project #<%= project_fields.index %> ... <% end %> ... <% end %>
--
这将允许您将 order
属性传递给您的数据库:
#app/controllers/posts_controller.rb
class PostsController < ApplicationController
def create
@post = Post.new post_params
@post.save
end
private
def post_params
params.require(:post).permit(meta_attributes: [:order, :textattr])
end
end
更新
我刚试过这种方法,可以确认它有效 (index
):
我唯一要说的是你的模型中可能没有 order
列,尽管它不应该返回合并错误。
我注意到您使用的是过时的 javascript 代码,您可以尝试不使用该代码看看会发生什么吗?
作为 order
列(或 position
- 调用列 order
会导致与生成的 SQL 混淆).
我认为主要问题是您的 coffeescript code。您需要为添加的每一行生成一个隐藏输入,并为您添加的行使用一个 current_index
计数器:
current_index = 0
addText = ->
html = """
<br>
<textarea name="post[things_attributes][#{current_index}][text]" id="post_things_attributes_#{current_index}_text"></textarea>
<input type="hidden" name="post[things_attributes][#{current_index}][order]" id="post_things_attributes_#{current_index}_order" value="#{current_index}" />
"""
$("#new_post input[type='submit']").before(html)
current_index += 1
addUrl = ->
html = """
<br>
<input type="url" name="post[things_attributes][#{current_index}][url]" id="post_things_attributes_#{current_index}_url">
<input type="hidden" name="post[things_attributes][#{current_index}][order]" id="post_things_attributes_#{current_index}_order" value="#{current_index}" />
"""
$("#new_post input[type='submit']").before(html)
current_index += 1
在您的 index.html.erb
中,按 order
列排序 Things
:
<% @posts.each do |post| %>
<% post.things.order(:order).each do |thing| %>
<br>
<%= thing.try(:text) %>
<%= thing.try(:url) %>
<% end %>
<br>
<% end %>
还值得注意的是,cocoon gem 很好地处理了这种动态嵌套表单模式。我建议您看一下并在您的项目中使用它。