Rails form rendering multiple times if using `.build`

287 views Asked by At

So I have a user form form_for that accepts nested attributes from account_prices. Whats happening is on my user controller I have this private method.

def pre_build_user_account_prices
        if @user.account_prices.empty?
          @accountable_default = @user.account_prices.build(status: 'default')
          @accountable_temporary = @user.account_prices.build(status: 'temporary')
        else
          @accountable_default = @user.account_prices.where(status: 'default')
          @accountable_temporary = @user.account_prices.where(status: 'temporary')
        end
      end

reason for the condition is, if I don't do a check here it will render 2 forms. an empty form and with data form. So checking is a need here


but my problem is this.Im on edit route, and when I try to submit an invalid form it renders multiple empty forms. heres an image.

enter image description here

if I kept submitting invalid form it will render multiple times. I was thinking if checking through JS if theres a multiple child I will remove it. is that the best approach?

here's my asscociations

Class User
  has_many :account_prices, as: :accountable, autosave: true
  accepts_nested_attributes_for :account_prices
end

polymorphic

 class AccountPrice
      enum status: {default: 'default', temporary: 'temporary'}

      validates :accountable, presence: true
      validates :status, presence: true
      validates :exp_start_date, presence: true, if: :is_temporary_status?
      validates :exp_end_date, presence: true, if: :is_temporary_status?

      belongs_to :accountable, polymorphic: true
      belongs_to :variant_price_set, class_name: "Spree::VariantPriceSet"
      belongs_to :shipping_method_price_set, class_name: "Spree::ShippingMethodPriceSet"

      def is_temporary_status?
        status == 'temporary'
      end

    end

user controller

Class UsersController
  before_action :pre_build_user_account_prices, only: :edit

  def update

        if @user.update_attributes(user_params)
          flash.now[:success] = Spree.t(:account_updated)
          redirect_to show_admin_user_path(@user)
        else
         render :edit
        end
   end

  def pre_build_user_account_prices
        if @user.account_prices.empty?
          @accountable_default = @user.account_prices.build(status: 'default')
          @accountable_temporary = @user.account_prices.build(status: 'temporary')
        else
          @accountable_default = @user.account_prices.where(status: 'default')
          @accountable_temporary = @user.account_prices.where(status: 'temporary')
        end
      end
end
1

There are 1 answers

2
Sujan Adiga On

I guess when you're trying @user.account_prices.where(... it is reading data which is not persisted to the db too. I mean the account_prices which was just build in the previous step.

Try,

def pre_build_user_account_prices
    if @user.account_prices.empty?
      @accountable_default = @user.account_prices.build(status: 'default')
      @accountable_temporary = @user.account_prices.build(status: 'temporary')
    else
      @accountable_default = @user.reload.account_prices.where(status: 'default')
      @accountable_temporary = @user.reload.account_prices.where(status: 'temporary')
    end
end

reload, reloads the object's attributes from the database

Also, when using accept_nested_attributes for to update an existing entry, you need to make sure that id of the nested object for which you're accepting nested attribute for is part of the attributes passed.