form_for collection_select 编辑时不显示保存的选择
form_for collection_select not showing saved selections on edit
我有一个用于新建和编辑的表单。表格填写了产品名称、描述等
此外,用户可以 select 嵌套下拉列表 (collection_select
) 中的项目,也可以创建新项目。在 "new" 上,表格工作正常 - 所有条目和 selections 保存。
当用户编辑已保存的产品时,表单会预加载该项目的所有填写条目,但不会预加载 collection_select 中的原始 selection。
并且,如果用户想要编辑该项目并决定创建一个新项目而不是之前 selected collection_select 项目,则会出现一条错误消息,说明该产品已经创建那个化学组。
对于这种双重困境的任何帮助将不胜感激。我是 RoR 的新手,我确定我在某处遗漏了一些东西。
这是我的表格
<%= render partial: 'layouts/errors', locals: {object: @product} %>
<%= form_for(@product) do |f| %>
<div>
<%= f.label :name %><br>
<%= f.text_field :name %><br>
<div>
<div>
<%= f.label :active_ingredient %><br>
<%= f.text_field :active_ingredient %><br>
<div>
<div>
<%= f.label :description %><br>
<%= f.text_area :description %><br>
</div>
<div>
<%= f.label :image %><br>
<%= f.file_field :image %><br>
</div>
<div>
<p>Select a Chemical Group:</p>
<%= f.collection_select :chem_group_id, ChemGroup.all, :id, :name, include_blank: 'Select One', selected: @product.chem_group, value: @product.chem_group.name %>
</div>
<div>
<p>Or, create a new Chemical Group:</p>
<!-- NESTED FORM! User writing attributes for another object. Use fields_for -->
<%= f.fields_for :chem_group do |cg| %>
<%= cg.label :name %>
<%= cg.text_field :name %>
<% end %>
</div>
<div>
<p>Select an Application Area:</p>
<%= f.collection_select :application_area_id, ApplicationArea.all, :id, :area_name, include_blank: 'Select One', selected: @product.application_area, value: @product.application_area.area_name %>
</div>
<div>
<p>Or, create a new Application Area:</p>
<!-- NESTED FORM! User writing attributes for another object. Use fields_for -->
<%= f.fields_for :application_area do |aa| %>
<%= aa.label :area_name %>
<%=aa.text_field :area_name %>
<% end %>
</div>
<br>
<%= f.submit "Save" %>
<% end %>
这是我的模型
class Product < ApplicationRecord
belongs_to :chem_group
belongs_to :application_area
belongs_to :user #admin creator
accepts_nested_attributes_for :chem_group #tells the model to accept chem_group attributes from cg nested form in new product form
accepts_nested_attributes_for :application_area
validates :active_ingredient, presence: true
validates :application_area, presence: true
validates :description, presence: true
validates :name, presence: true
validate :not_a_duplicate #checking for what we DON'T WANT
def chem_group_attributes=(attributes)
self.chem_group = ChemGroup.find_or_create_by(attributes) if !attributes['name'].empty?
self.chem_group
end
def application_area_attributes=(attributes)
self.application_area = ApplicationArea.find_or_create_by(attributes) if !attributes['area_name'].empty?
self.application_area
end
#if there is already a product with that name && chem_group, give error
def not_a_duplicate
#calling the instance of the attribute [string/integer: key]
if Product.find_by(name: name, chem_group_id: chem_group_id)
errors.add(:name, 'has already been created for that Chemical Group')
end
end
end
这是我的控制器
class ProductsController < ApplicationController
def new
if logged_in?
@product = Product.new
1.times {@product.build_chem_group} #for the nested form. Builds the chem_group attributes
@product.build_application_area
else
flash[:error] = "Sorry, you must be logged in to create a new product."
redirect_to products_path
end
end
def create
@product = Product.new(product_params)
@product.user_id = session[:user_id] #bc product belongs_to user. user_id required from model
if @product.save #validation
# @product.image.purge
# @product.image.attach(params[:product][:image]) # allows image to be replaced if user changes image
redirect_to product_path(@product)
else
@product.build_chem_group
@product.build_application_area
render :new
end
end
def edit
find_product
1.times {@product.build_chem_group}
if @product.user != current_user
flash[:error] = "Sorry, you can only edit your own products"
redirect_to products_path
end
end
def update
find_product
if @product.update(product_params)
redirect_to product_path(@product)
else
render :edit
end
end
private
def product_params
params.require(:product).permit(:name, :description, :active_ingredient, :image, :chem_group_id, :application_area_id, chem_group_attributes: [:id, :name], application_area_attributes: [:id, :area_name])
#chem_group_id and chem_group_attributes [:name] is permitting elements from new product form
end
def find_product
@product = Product.find_by(id: params[:id])
end
end
正在阅读您提供的表格:
- 如果您正在编辑产品,则集合应该显示之前的
选择
chem_group
,并填写创建应填写
它的名字,因为它是同一个关联对象。不是因为你
弄乱了 selected
和 value
选项。 selected
在
collection_select
这样的应该指向id
不要反对。
喜欢:selected: @product.chem_group.id
但真的没必要
这里。您应该跳过这些选项 (selected
nad value
因为您正在为 @product
). 使用表单生成器
- 在有人尝试用新值编辑
chem_group
后填写
真正的混乱开始了,因为在 select 字段中仍然有一个 id
指向前一个 chem_group
和 chem_group_attributes
只有 :name
我现在猜测 rails 将保存它
chem_group_id
并且您的自定义验证将失败
现有对象(看起来这个验证永远不会让你
保存对象而不改变 chem_group
因为它很明显
现有 - 您正在编辑它!)
试试看:
- 更改或删除自定义验证。或者检查是否在中找到对象
验证不是您正在编辑但之前已保存的验证
- 默认禁用输入之一 (
<%= cg.text_field :name,
disabled: @product.chem_group.present? %>
) 并切换它们
取决于用户在做什么 - selecting 或打字(例如使用
复选框和 JS onchange)。这是为了确定你是什么参数
通过.
- 另外:
chem_group_attributes: [:id, :name]
拜托,你没有通过
这里有一个 ID - 删除它。
def chem_group_attributes=(attributes)
覆盖是危险的。我
知道你想要得到什么,但它可能不是最多的
实现这一目标的绝妙方式。
通常您可能还希望看到:https://select2.org/tagging。它仍然是和你在这里一样复杂的逻辑,还有一些 JS 可以使标签在创建新的关联对象时很好地工作,但它看起来更好,具有更好的用户体验,而且我去过那里 - 我正在使用它 accepts_nested_attributes_for 一般情况下,你可以打我例子。
我有一个用于新建和编辑的表单。表格填写了产品名称、描述等
此外,用户可以 select 嵌套下拉列表 (collection_select
) 中的项目,也可以创建新项目。在 "new" 上,表格工作正常 - 所有条目和 selections 保存。
当用户编辑已保存的产品时,表单会预加载该项目的所有填写条目,但不会预加载 collection_select 中的原始 selection。
并且,如果用户想要编辑该项目并决定创建一个新项目而不是之前 selected collection_select 项目,则会出现一条错误消息,说明该产品已经创建那个化学组。 对于这种双重困境的任何帮助将不胜感激。我是 RoR 的新手,我确定我在某处遗漏了一些东西。
这是我的表格
<%= render partial: 'layouts/errors', locals: {object: @product} %>
<%= form_for(@product) do |f| %>
<div>
<%= f.label :name %><br>
<%= f.text_field :name %><br>
<div>
<div>
<%= f.label :active_ingredient %><br>
<%= f.text_field :active_ingredient %><br>
<div>
<div>
<%= f.label :description %><br>
<%= f.text_area :description %><br>
</div>
<div>
<%= f.label :image %><br>
<%= f.file_field :image %><br>
</div>
<div>
<p>Select a Chemical Group:</p>
<%= f.collection_select :chem_group_id, ChemGroup.all, :id, :name, include_blank: 'Select One', selected: @product.chem_group, value: @product.chem_group.name %>
</div>
<div>
<p>Or, create a new Chemical Group:</p>
<!-- NESTED FORM! User writing attributes for another object. Use fields_for -->
<%= f.fields_for :chem_group do |cg| %>
<%= cg.label :name %>
<%= cg.text_field :name %>
<% end %>
</div>
<div>
<p>Select an Application Area:</p>
<%= f.collection_select :application_area_id, ApplicationArea.all, :id, :area_name, include_blank: 'Select One', selected: @product.application_area, value: @product.application_area.area_name %>
</div>
<div>
<p>Or, create a new Application Area:</p>
<!-- NESTED FORM! User writing attributes for another object. Use fields_for -->
<%= f.fields_for :application_area do |aa| %>
<%= aa.label :area_name %>
<%=aa.text_field :area_name %>
<% end %>
</div>
<br>
<%= f.submit "Save" %>
<% end %>
这是我的模型
class Product < ApplicationRecord
belongs_to :chem_group
belongs_to :application_area
belongs_to :user #admin creator
accepts_nested_attributes_for :chem_group #tells the model to accept chem_group attributes from cg nested form in new product form
accepts_nested_attributes_for :application_area
validates :active_ingredient, presence: true
validates :application_area, presence: true
validates :description, presence: true
validates :name, presence: true
validate :not_a_duplicate #checking for what we DON'T WANT
def chem_group_attributes=(attributes)
self.chem_group = ChemGroup.find_or_create_by(attributes) if !attributes['name'].empty?
self.chem_group
end
def application_area_attributes=(attributes)
self.application_area = ApplicationArea.find_or_create_by(attributes) if !attributes['area_name'].empty?
self.application_area
end
#if there is already a product with that name && chem_group, give error
def not_a_duplicate
#calling the instance of the attribute [string/integer: key]
if Product.find_by(name: name, chem_group_id: chem_group_id)
errors.add(:name, 'has already been created for that Chemical Group')
end
end
end
这是我的控制器
class ProductsController < ApplicationController
def new
if logged_in?
@product = Product.new
1.times {@product.build_chem_group} #for the nested form. Builds the chem_group attributes
@product.build_application_area
else
flash[:error] = "Sorry, you must be logged in to create a new product."
redirect_to products_path
end
end
def create
@product = Product.new(product_params)
@product.user_id = session[:user_id] #bc product belongs_to user. user_id required from model
if @product.save #validation
# @product.image.purge
# @product.image.attach(params[:product][:image]) # allows image to be replaced if user changes image
redirect_to product_path(@product)
else
@product.build_chem_group
@product.build_application_area
render :new
end
end
def edit
find_product
1.times {@product.build_chem_group}
if @product.user != current_user
flash[:error] = "Sorry, you can only edit your own products"
redirect_to products_path
end
end
def update
find_product
if @product.update(product_params)
redirect_to product_path(@product)
else
render :edit
end
end
private
def product_params
params.require(:product).permit(:name, :description, :active_ingredient, :image, :chem_group_id, :application_area_id, chem_group_attributes: [:id, :name], application_area_attributes: [:id, :area_name])
#chem_group_id and chem_group_attributes [:name] is permitting elements from new product form
end
def find_product
@product = Product.find_by(id: params[:id])
end
end
正在阅读您提供的表格:
- 如果您正在编辑产品,则集合应该显示之前的
选择
chem_group
,并填写创建应填写 它的名字,因为它是同一个关联对象。不是因为你 弄乱了selected
和value
选项。selected
在collection_select
这样的应该指向id
不要反对。 喜欢:selected: @product.chem_group.id
但真的没必要 这里。您应该跳过这些选项 (selected
nadvalue
因为您正在为@product
). 使用表单生成器
- 在有人尝试用新值编辑
chem_group
后填写 真正的混乱开始了,因为在 select 字段中仍然有一个 id 指向前一个chem_group
和chem_group_attributes
只有:name
我现在猜测 rails 将保存它chem_group_id
并且您的自定义验证将失败 现有对象(看起来这个验证永远不会让你 保存对象而不改变chem_group
因为它很明显 现有 - 您正在编辑它!)
试试看:
- 更改或删除自定义验证。或者检查是否在中找到对象 验证不是您正在编辑但之前已保存的验证
- 默认禁用输入之一 (
<%= cg.text_field :name, disabled: @product.chem_group.present? %>
) 并切换它们 取决于用户在做什么 - selecting 或打字(例如使用 复选框和 JS onchange)。这是为了确定你是什么参数 通过. - 另外:
chem_group_attributes: [:id, :name]
拜托,你没有通过 这里有一个 ID - 删除它。 def chem_group_attributes=(attributes)
覆盖是危险的。我 知道你想要得到什么,但它可能不是最多的 实现这一目标的绝妙方式。
通常您可能还希望看到:https://select2.org/tagging。它仍然是和你在这里一样复杂的逻辑,还有一些 JS 可以使标签在创建新的关联对象时很好地工作,但它看起来更好,具有更好的用户体验,而且我去过那里 - 我正在使用它 accepts_nested_attributes_for 一般情况下,你可以打我例子。