简体   繁体   中英

Rails 4 nested form not loading values on show/edit

I have a form for orders with a nested form for products. Products save to the database properly, but on show/edit product values aren't loaded - the fields display but are not populated.

order model:

has_many :products, dependent: :destroy
accepts_nested_attributes_for :products

product model:

belongs_to :order

orders_controller:

before_action :set_order, only: [:show, :edit]

def new
  @order = Order.new
  @order.products.build
end

def show
  # empty
end

def edit
  # empty
end

def order_params
  params.require(:order).permit(
    :number,
    :price,
    products_attributes: [
      :id,
      :type,
      :color
    ])
end

def set_order
  if params[:id].present? && user_signed_in?
    @order = Order.find(params[:id])
  elsif params[:number].present? && params[:email].present?
    @order = Order.find_by(number: params[:number], email: params[:email])
  else
    redirect_to welcome_index_path, alert: I18n.t('views.welcome.index.no_order_found')
  end
end

routes.rb:

resources :orders do
  get 'express', on: :new
end
post 'orders/status', to: 'orders#show'

_form.html.erb (used by both edit and show):

<%= form_for @order do |f| %>
  <%= f.text_field(:number) %>
  # etc.
  <%= f.fields_for :products do |g| %>
    <%= g.text_field(:color) %>
    # etc.
  <% end %>
<% end %>

Your code seems good and it should works. Can you please try the following change to pass @products in form fields

<%= f.fields_for :products, @products do |g| %>
   <%= g.text_field(:color) %>
   # etc.
<% end %>

And simply make @products variable available in new and edit action. Like following

def new
  #your code goes here ...
  @products = @order.products.build
end


def edit
  #your code goes here ...
  @products = @order.products
end

I finally figured out the problem - it was in my custom FormBuilder class. I didn't include it in the original question because I didn't think it was relevant. My form actually looks more like this:

<%= f.fields_for :products, builder: MyFormBuilder do |g| %>
  <%= g.product_select(:color) %>
  <%= g.product_checkbox(:accessories) %>
  # etc.
<% end %>

MyFormBuilder had the following methods:

def product_select(label)        
  tags = ''
  Product.send(label.to_s.pluralize).each do |a|
    tags += @template.content_tag(:option, a[:text], value: a[:value], data: a[:data])
  end
  tag_with_label(label) do
    @template.select(@object_name, label, nil, {}, options) { tags.html_safe }
  end
end

def product_checkbox(label)
  tag_with_label("#{label.to_s}?") do
    @template.check_box(@object_name, label)
  end
end

The problem was that nothing was specifying how to load saved attributes for an existing object. Here are the fixed versions of the methods:

def product_select(label)
  choices = @template.options_for_select(Product.send(label.to_s.pluralize), @object.send(label))
  tag_with_label(label) do
    @template.select(@object_name, label, choices, {}, options)
  end
end

def product_checkbox(label)
  tag_with_label("#{label.to_s}?") do
    @template.check_box_tag("#{@object_name}[#{label}]", 1, @object.send(label)) 
  end
end

The key part was @object.send(label) which was a method call to retrieve the appropriate attribute out of the database. I also changed the Product model to return an array in the format expected by options_for_select (an array of arrays).

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM