rails 5个带茧嵌套形式的记录一次性创建
rails 5 with cocoon nested form records creation all at once
我想知道是否可能,给出以下场景,使用 has_many 在连接 table 中创建记录:通过 Rails 5 中的关系与 cocoon 和 simple_form 使用尚未创建的记录的 :id。我问这个,因为我发现在 "join-table" 中创建这些记录的唯一方法是编辑产品然后添加输入,但为了用户体验,我认为一次完成所有操作会更好,输入新产品参数,添加输入并在提交表单后,使用其 ID 创建产品,链接输入的 join-table 记录等:
场景:
Given i open a new form to create a "product"
And i fill the "product" parameters in the form
And i add with cocoon a dynamic field to link the product with an "input" that exists in order to create a new record in the join table "product_inputs"
And i click the "submit" button
Then the ID of the product created is submited in the new "product_inputs" table record, linked with the "input" id
我设置的模型如下:
class Product < ApplicationRecord
belongs_to :user, inverse_of: :products
has_many :product_inputs
has_many :inputs, through: :product_inputs, :class_name => 'Input'
accepts_nested_attributes_for :inputs
accepts_nested_attributes_for :product_inputs, :reject_if => :all_blank, :allow_destroy => true
validates :name, :presence => true
validates :description, :presence => true
validates_presence_of :user_id
end
class Input < ApplicationRecord
belongs_to :user, inverse_of: :inputs
has_many :input_providers
has_many :providers, through: :input_providers
has_many :product_inputs
has_many :products, through: :product_inputs
accepts_nested_attributes_for :products
accepts_nested_attributes_for :product_inputs, :reject_if => :all_blank
accepts_nested_attributes_for :providers
accepts_nested_attributes_for :input_providers, :reject_if => :all_blank, :allow_destroy => true
validates :name, :presence => true
validates :description, :presence => true
validates_presence_of :user_id
end
class ProductInput < ApplicationRecord
belongs_to :product
belongs_to :input
accepts_nested_attributes_for :input, :reject_if => :all_blank
accepts_nested_attributes_for :product, :reject_if => :all_blank
validates_presence_of :input
validates_presence_of :product
validates :quantity, numericality: { only_integer: true }, :allow_nil => true
end
产品控制器所需参数:
def product_params
parameters = params.require(:product).permit(:id, :name, :description, :stock, :price, :user_id,
product_inputs_attributes: [:id, :input_id, :product_id, :quantity, :_destroy,
input_attributes: [:id, :name, :description, :_destroy]])
parameters[:user_id] = current_user.id
parameters
end
产品_形式:
<%= simple_form_for @product, :html => { class: "smart-form product_validations" } do |f| %>
<fieldset>
<div class="row">
<section class="col col-4">
<%= f.input :name, placeholder: 'Product Name', label: false %>
</section>
<section class="col col-4">
<%= f.input :description, placeholder: 'Enter Description', label: false %>
</section>
<section class="col col-4">
<%= f.input :stock, placeholder: 'Stock', label: false %>
</section>
</div>
<% if params[:id] %>
<div class="row">
<section class="col col-6">
<%= link_to_add_association 'add an input', f, :product_inputs, class: 'btn btn-success btn-sm' %>
</section>
</div>
<div class="row smart-form">
<section id="inputs" class="col col-12">
<%= f.simple_fields_for :product_inputs do |product_input| %>
<%= render 'product_input_fields', :f => product_input %>
<% end %>
</section>
</div>
<% end %>
</fieldset>
<footer>
<%= link_to 'Cancel', products_path, class: 'btn btn-default' %>
<%= f.button :submit, :class => 'btn btn-primary' %>
</footer>
<% end %>
_product_input_fields.html.erb
<div class="nested-fields product_input-fields">
<fieldset>
<div class="row">
<section class="input_from_list col col-4">
<%= f.association :input, :required => true,
collection: Input.order(:name),
prompt: 'Choose an existing input', label: false %>
</section>
<section class="nested-fields col col-4">
<%= f.input :quantity, :required => true, :placeholder => 'Enter the input quantity', :label => false %>
</section>
<section class="col col-4">
<%= link_to_remove_association f, class: 'remove-tag btn btn-danger btn-xs' do %>
<div class="glyphicon glyphicon-remove"></div>
<% end %>
</section>
</div>
<div class="row">
<section class="col col-4">
<%= link_to_add_association 'or create a new input', f, :input, class: 'btn btn-default btn-xs add-input' %>
</section>
</div>
</fieldset>
</div>
_input_fields.html.erb
<div class="nested-fields">
<section class="col col-6">
<div class="new_input">
<%= f.input :name, :placeholder => 'Create this new input', :label => false %>
</div>
</section>
<section class="col col-6">
<div class="new_input">
<%= f.input :description, :placeholder => 'Enter a Description', :label => false %>
</div>
</section>
<%= link_to 'Cancel', class: 'btn btn-default' %>
</div>
Product.js
$('#inputs').on('cocoon:after-insert',
function (e, input) {
console.log('inserting new input ...');
$(".product-input-fields a.add-tag").data("association-insertion-position", 'after').data("association-insertion-node", 'this');
$(this).find('.product-input-fields').bind('cocoon:after-insert',
function () {
console.log('insert new input ...');
console.log($(this));
$(this).find(".input_from_list").remove();
$(this).find("a.add_fields").hide();
});
});
$('.product-input-fields').bind('cocoon:after-insert',
function (e) {
console.log('replace OLD input ...');
e.stopPropagation();
console.log($(this));
$(this).find(".input_from_list").remove();
$(this).find("a.add_fields").hide();
});
根据您的代码,您确实检查了 example project and I can verify that that works for rails 5. However in your code I see you are missing the inverse_of:
specifiers of the has_many
associations. See recent commits
Rails 5 已更改,因此默认需要 belongs_to
关系。保存新项目时,id
仅在保存后设置,因此验证将失败。但是,正确指定 inverse_of
rails 可以正确验证您的新项目。
我想知道是否可能,给出以下场景,使用 has_many 在连接 table 中创建记录:通过 Rails 5 中的关系与 cocoon 和 simple_form 使用尚未创建的记录的 :id。我问这个,因为我发现在 "join-table" 中创建这些记录的唯一方法是编辑产品然后添加输入,但为了用户体验,我认为一次完成所有操作会更好,输入新产品参数,添加输入并在提交表单后,使用其 ID 创建产品,链接输入的 join-table 记录等:
场景:
Given i open a new form to create a "product"
And i fill the "product" parameters in the form
And i add with cocoon a dynamic field to link the product with an "input" that exists in order to create a new record in the join table "product_inputs"
And i click the "submit" button
Then the ID of the product created is submited in the new "product_inputs" table record, linked with the "input" id
我设置的模型如下:
class Product < ApplicationRecord
belongs_to :user, inverse_of: :products
has_many :product_inputs
has_many :inputs, through: :product_inputs, :class_name => 'Input'
accepts_nested_attributes_for :inputs
accepts_nested_attributes_for :product_inputs, :reject_if => :all_blank, :allow_destroy => true
validates :name, :presence => true
validates :description, :presence => true
validates_presence_of :user_id
end
class Input < ApplicationRecord
belongs_to :user, inverse_of: :inputs
has_many :input_providers
has_many :providers, through: :input_providers
has_many :product_inputs
has_many :products, through: :product_inputs
accepts_nested_attributes_for :products
accepts_nested_attributes_for :product_inputs, :reject_if => :all_blank
accepts_nested_attributes_for :providers
accepts_nested_attributes_for :input_providers, :reject_if => :all_blank, :allow_destroy => true
validates :name, :presence => true
validates :description, :presence => true
validates_presence_of :user_id
end
class ProductInput < ApplicationRecord
belongs_to :product
belongs_to :input
accepts_nested_attributes_for :input, :reject_if => :all_blank
accepts_nested_attributes_for :product, :reject_if => :all_blank
validates_presence_of :input
validates_presence_of :product
validates :quantity, numericality: { only_integer: true }, :allow_nil => true
end
产品控制器所需参数:
def product_params
parameters = params.require(:product).permit(:id, :name, :description, :stock, :price, :user_id,
product_inputs_attributes: [:id, :input_id, :product_id, :quantity, :_destroy,
input_attributes: [:id, :name, :description, :_destroy]])
parameters[:user_id] = current_user.id
parameters
end
产品_形式:
<%= simple_form_for @product, :html => { class: "smart-form product_validations" } do |f| %>
<fieldset>
<div class="row">
<section class="col col-4">
<%= f.input :name, placeholder: 'Product Name', label: false %>
</section>
<section class="col col-4">
<%= f.input :description, placeholder: 'Enter Description', label: false %>
</section>
<section class="col col-4">
<%= f.input :stock, placeholder: 'Stock', label: false %>
</section>
</div>
<% if params[:id] %>
<div class="row">
<section class="col col-6">
<%= link_to_add_association 'add an input', f, :product_inputs, class: 'btn btn-success btn-sm' %>
</section>
</div>
<div class="row smart-form">
<section id="inputs" class="col col-12">
<%= f.simple_fields_for :product_inputs do |product_input| %>
<%= render 'product_input_fields', :f => product_input %>
<% end %>
</section>
</div>
<% end %>
</fieldset>
<footer>
<%= link_to 'Cancel', products_path, class: 'btn btn-default' %>
<%= f.button :submit, :class => 'btn btn-primary' %>
</footer>
<% end %>
_product_input_fields.html.erb
<div class="nested-fields product_input-fields">
<fieldset>
<div class="row">
<section class="input_from_list col col-4">
<%= f.association :input, :required => true,
collection: Input.order(:name),
prompt: 'Choose an existing input', label: false %>
</section>
<section class="nested-fields col col-4">
<%= f.input :quantity, :required => true, :placeholder => 'Enter the input quantity', :label => false %>
</section>
<section class="col col-4">
<%= link_to_remove_association f, class: 'remove-tag btn btn-danger btn-xs' do %>
<div class="glyphicon glyphicon-remove"></div>
<% end %>
</section>
</div>
<div class="row">
<section class="col col-4">
<%= link_to_add_association 'or create a new input', f, :input, class: 'btn btn-default btn-xs add-input' %>
</section>
</div>
</fieldset>
</div>
_input_fields.html.erb
<div class="nested-fields">
<section class="col col-6">
<div class="new_input">
<%= f.input :name, :placeholder => 'Create this new input', :label => false %>
</div>
</section>
<section class="col col-6">
<div class="new_input">
<%= f.input :description, :placeholder => 'Enter a Description', :label => false %>
</div>
</section>
<%= link_to 'Cancel', class: 'btn btn-default' %>
</div>
Product.js
$('#inputs').on('cocoon:after-insert',
function (e, input) {
console.log('inserting new input ...');
$(".product-input-fields a.add-tag").data("association-insertion-position", 'after').data("association-insertion-node", 'this');
$(this).find('.product-input-fields').bind('cocoon:after-insert',
function () {
console.log('insert new input ...');
console.log($(this));
$(this).find(".input_from_list").remove();
$(this).find("a.add_fields").hide();
});
});
$('.product-input-fields').bind('cocoon:after-insert',
function (e) {
console.log('replace OLD input ...');
e.stopPropagation();
console.log($(this));
$(this).find(".input_from_list").remove();
$(this).find("a.add_fields").hide();
});
根据您的代码,您确实检查了 example project and I can verify that that works for rails 5. However in your code I see you are missing the inverse_of:
specifiers of the has_many
associations. See recent commits
Rails 5 已更改,因此默认需要 belongs_to
关系。保存新项目时,id
仅在保存后设置,因此验证将失败。但是,正确指定 inverse_of
rails 可以正确验证您的新项目。