John Fadria
John Fadria

Reputation: 1945

Rails rendering nested attributes not showing fields when error validation

I have a fully working nested attributes form. But my problem is that when a validation fails the nested form fields are not shown after the render:

At first

enter image description here

After the validation fails, Location name and Bin name disappear:

enter image description here

In the model:

accepts_nested_attributes_for :location, reject_if: proc { |l| l[:name].blank? }

The partial _form.html.erb

<%= form_with(model: inventory_item, local: true) do |form| %>
  <% if inventory_item.errors.any? %>
    <div id="error_explanation">
      <h2><%= pluralize(inventory_item.errors.count, "error") %> prohibited this inventory_item from being saved:</h2>

      <ul>
        <% inventory_item.errors.each do |error| %>
          <li><%= error.full_message %></li>
        <% end %>
      </ul>
    </div>
  <% end %>

  <%= form.fields_for :location do |location| %>
    <div class="field">
      <%= location.label :location_name %>
      <%= location.text_field :name %>
    </div>
      <%= location.fields_for :bins do |bin| %>
        <div class="field">
          <%= bin.label :bin_name %>
          <%= bin.text_field :name %>
        </div>
      <% end %>
  <% end %>

  <div class="actions">
    <%= form.submit %>
  </div>
<% end %>

The controller:

  def new
    @inventory_item = InventoryItem.new
    @inventory_item.build_location.bins.build
  end

  def create
    @inventory_item = InventoryItem.new(inventory_item_params)

    respond_to do |format|
      if @inventory_item.save
        format.html { redirect_to @inventory_item, notice: "Inventory item was successfully created." }
        format.json { render :show, status: :created, location: @inventory_item }
      else
        format.html { render :new, status: :unprocessable_entity }
        format.json { render json: @inventory_item.errors, status: :unprocessable_entity }
      end
    end
  end

def inventory_item_params
  params.require(:inventory_item).permit(:location_id, :bin_id, location_attributes:[:name, bins_attributes:[:name]])
end

Thanks in advance

Upvotes: 0

Views: 836

Answers (1)

Deepesh
Deepesh

Reputation: 6418

The issue is your @inventory_item object which doesn't have the location object associated. When the new action is called you build the location object like this:

@inventory_item.build_location.bins.build

So you will have to do something similar for the create action when there is an error. You could do something like this:

  def new
    @inventory_item = InventoryItem.new
    build_location
  end

  def create
    @inventory_item = InventoryItem.new(inventory_item_params)

    respond_to do |format|
      if @inventory_item.save
        format.html { redirect_to @inventory_item, notice: "Inventory item was successfully created." }
        format.json { render :show, status: :created, location: @inventory_item }
      else
        build_location #<----- call the method here and if the location doesn't exist then build a new object
        format.html { render :new, status: :unprocessable_entity }
        format.json { render json: @inventory_item.errors, status: :unprocessable_entity }
      end
    end
  end

  def build_location
    return if @inventory_item.location.present?

    @inventory_item.build_location.bins.build
  end

Upvotes: 2

Related Questions