我已经围绕这个圈了几个圈子。
Regions
我在更新时遇到问题Listing
。
现在我什至无法在创建时Regions
添加多个。Listing
如果有人可以帮助我解决问题,那就太好了。从新鲜(有经验的)眼睛看我的代码可能会注意到我在做什么是愚蠢的。
- 两种型号:
Listing
和Region
- 第三种加盟模式:
Regionalization
楷模:
# app/models/listing.rb
class Listing < ApplicationRecord
has_many :regionalizations
has_many :regions, through: :regionalizations
accepts_nested_attributes_for :regionalizations, allow_destroy: true, reject_if: :all_blank
end
# app/models/region.rb
class Region < ApplicationRecord
has_many :regionalizations
has_many :listings, through: :regionalizations
end
# app/models/regionalization.rb
class Regionalization < ApplicationRecord
belongs_to :listing
belongs_to :region
end
模型和关联对我来说似乎是合理的。我认为问题在于控制器和/或嵌套形式。
控制器操作 [注意我正在为此控制器使用仪表板命名空间]
class Dashboard::ListingsController < Dashboard::BaseController
def new
@listing = Listing.new
end
def create
@listing = Listing.new(listing_params)
@listing.user_id = current_user.id
@listing.regionalizations.build
if @listing.save
redirect_to dashboard_path, notice: "Your Listing was created successfuly"
else
render :new
end
end
def update
respond_to do |format|
if @listing.update(listing_params)
format.html { redirect_to edit_dashboard_listing_path(@listing), notice: 'Your Listing was successfully updated.' }
format.json { render :show, status: :ok, location: @listing }
else
format.html { render :edit }
format.json { render json: @listing.errors, status: :unprocessable_entity }
end
end
end
private
def listing_params
params.require(:listing).permit(:id, :name, :excerpt, :description, :email, :website, :phone_number, :user_id, :featured_image, :business_logo, :address, :category_id, :facebook, :instagram, :twitter, :status, :regionalization_id, gallery_images: [], regionalizations_attributes: [:id, :region_id, :listing_id, :_destroy])
end
end
仪表板/列表/_form:
<%= form_with(model: [:dashboard, listing], local: true) do |f| %>
<article class="card mb-3">
<div class="card-body">
<h5 class="card-title mb-4">Delivery Regions</h5>
<%= f.fields_for :regionalizations do |regionalizations_form| %>
<%= render 'regionalization_fields', f: regionalizations_form %>
<% end %>
<%= link_to_add_fields "Add Region", f, :regionalizations %>
</div>
</article>
<%= f.submit data: { turbolinks: false }, class: "btn btn-outline-primary" %>
<% end %>
_regionalization_fields.html.erb:
<p class="nested-fields">
<%= f.collection_select(:region_id, Region.all, :id, :name, {multiple: true}, {class: 'form-control'}) %>
<%= f.hidden_field :_destroy %>
<%= link_to "Remove", '#', class: "remove_fields" %>
</p>
创建新时验证错误Listing
:
Regionalizations region must exist
如果我将它添加到区域化表中,我可以让区域化工作。
belongs_to :region, optional: true
现在我的参数只显示一个区域化属性,除非我告诉它构建 3 或 4。
像这样:
4.times do @listing.regionalizations.build end
我已经使用Steve Polito 的指南来尝试让它工作。我没有更改任何 javascript 内容或 application_helper 内容。
添加和删除字段在前端工作正常。删除嵌套字段在 dB 中工作正常。
请问我在这里错过了什么完全愚蠢的东西吗?
我唯一能注意到新嵌套字段和从构建方法中引入的任何不同之处是“选定”标签不在添加到表单的新嵌套字段上。
提交参数:
Started POST "/dashboard/listings" for ::1 at 2020-10-30 20:22:15 +0000
Processing by Dashboard::ListingsController#create as HTML
Parameters: {"authenticity_token"=>"shtfCS/cSj/w/I6S1tNey99L8TKf48Xj0GAOMsODU3l44o0pJdjucCteQXca496aosNCEp7sPD85UM4QO4jEnw==", "listing"=>{"name"=>"", "excerpt"=>"", "description"=>"", "category_id"=>"1", "email"=>"", "phone_number"=>"", "website"=>"", "address"=>"", "facebook"=>"#", "instagram"=>"#", "twitter"=>"#", "regionalizations_attributes"=>{"0"=>{"region_id"=>"14", "_destroy"=>"false"}}}, "commit"=>"Create Listing"}
我将添加从 Steve 的嵌套表单教程中获取的 applicaton_helper 文件。其中一条评论提到了代码的动态能力。它有效(只是不适合我)。我可以通过强制编号循环来实现我对 create 方法的需要。只是无法让字段动态添加到数据库中。
# This method creates a link with `data-id` `data-fields` attributes. These attributes are used to create new instances of the nested fields through Javascript.
def link_to_add_fields(name, f, association)
# Takes an object (@person) and creates a new instance of its associated model (:addresses)
# To better understand, run the following in your terminal:
# rails c --sandbox
# @person = Person.new
# new_object = @person.send(:addresses).klass.new
new_object = f.object.send(association).klass.new
# Saves the unique ID of the object into a variable.
# This is needed to ensure the key of the associated array is unique. This is makes parsing the content in the `data-fields` attribute easier through Javascript.
# We could use another method to achive this.
id = new_object.object_id
# https://api.rubyonrails.org/ fields_for(record_name, record_object = nil, fields_options = {}, &block)
# record_name = :addresses
# record_object = new_object
# fields_options = { child_index: id }
# child_index` is used to ensure the key of the associated array is unique, and that it matched the value in the `data-id` attribute.
# `person[addresses_attributes][child_index_value][_destroy]`
fields = f.fields_for(association, new_object, child_index: id) do |builder|
# `association.to_s.singularize + "_fields"` ends up evaluating to `address_fields`
# The render function will then look for `views/people/_address_fields.html.erb`
# The render function also needs to be passed the value of 'builder', because `views/people/_address_fields.html.erb` needs this to render the form tags.
render(association.to_s.singularize + "_fields", f: builder)
end
# This renders a simple link, but passes information into `data` attributes.
# This info can be named anything we want, but in this case we chose `data-id:` and `data-fields:`.
# The `id:` is from `new_object.object_id`.
# The `fields:` are rendered from the `fields` blocks.
# We use `gsub("\n", "")` to remove anywhite space from the rendered partial.
# The `id:` value needs to match the value used in `child_index: id`.
link_to(name, '#', class: "add_fields", data: {id: id, fields: fields.gsub("\n", "")})
end