0

I am trying to deal in form with 2 level nested resource. My parrent resource is supplier. I've done adding (building) new descedants (address) in new ad edit form. I've also done adding (building) descedant of descedant (contacts) in new form but I have problem with adding descedant of descedant (contacts) in edit form. In edit form I want to add submit button [Add contact] after each contact and when user click on this button go to update method and build new contact instance in proper address -I need send with [Add contact] submit param with address ID. I have no idea how to do it. I've try hidden field but it send last hidden fiel value from loop. It is any chance to send diffrent param with submit button?

I have 2 level nested resource. Supplier has descedant -> address Address has descedant -> contact

Models: Supplier:

has_many :supplier_addresses
   has_many :addresses, :through => :supplier_addresses
   accepts_nested_attributes_for :addresses, 
                            :allow_destroy => true,
                            :reject_if     => :all_blank  

Address:

has_many :address_contacts
   has_many :contacts, :through => :address_contacts
   accepts_nested_attributes_for :contacts, 
                            :allow_destroy => true,
                            :reject_if     => :all_blank

Contact:

 has_many :address_contacts
   has_many :addresses, :through => :address_contacts

Suppliers_Controller:

 class SuppliersController < ApplicationController

   before_action :set_supplier, only: [:show, :edit, :update, :destroy]

   def index
      if params[:search]
        @suppliers = Supplier.paginate(page: params[:page], :per_page => 15).by_letter(params[:search]) 

      else
        @suppliers = Supplier.paginate(page: params[:page], :per_page => 15).order(:name)
      end
   end

  # GET /currencies/new
  def new
    @supplier = Supplier.new
    ad=@supplier.addresses.build
    ad.contacts.build
  end

  def show
  end

  def edit
    ad=@supplier.addresses.build
    ad.contacts.build
  end

   # POST /currencies
  # POST /currencies.json
  def create
    @supplier = Supplier.new(supplier_params)
    if add_address?
      @supplier.addresses.build
      respond_to do |format|
        format.html { render :new }
        format.json { render json: @supplier.errors, status: :unprocessable_entity }
      end
    elsif add_contact?
      @supplier.addresses[params[:add_id].to_i].contacts.build
      respond_to do |format|
        format.html { render :new }
        format.json { render json: @supplier.errors, status: :unprocessable_entity }
      end
    else
      respond_to do |format|
        if @supplier.save
          flash[:info] = 'Supplier was successfully created.'
          format.html { redirect_to @supplier }
          format.json { render :show, status: :created, location: @supplier }
        else
          format.html { render :new }
          format.json { render json: @supplier.errors, status: :unprocessable_entity }
        end
      end
    end
  end

  # PATCH/PUT /currencies/1
  # PATCH/PUT /currencies/1.json
  def update
    if add_address?
      @supplier.addresses.build
      respond_to do |format|
        format.html { render :edit }
        format.json { render json: @supplier.errors, status: :unprocessable_entity }
      end
    elsif add_contact?
      @supplier.addresses[params[:add_id].to_i].contacts.build
      respond_to do |format|
        format.html { render :edit }
        format.json { render json: @supplier.errors, status: :unprocessable_entity }
      end
    else
      respond_to do |format|
        if @supplier.update(supplier_params)
          flash[:info] = 'Supplier was successfully updated.'
          format.html { redirect_to supplier_url }
          format.json { render :show, status: :ok, location: @supplier }
        else
          format.html { render :edit }
          format.json { render json: @supplier.errors, status: :unprocessable_entity }
        end
      end
    end
  end

  # DELETE /currencies/1
  # DELETE /currencies/1.json
  def destroy
    @supplier.destroy
    respond_to do |format|
      flash[:info] = 'Supplier was successfully deleted.'
      format.html { redirect_to suppliers_url }
      format.json { head :no_content }
    end
  end

   private
    # Use callbacks to share common setup or constraints between actions.
    def set_supplier
      @supplier = Supplier.find(params[:id])
    end

    #for new add
    def add_address?
      params[:commit] == "Add address"
    end

    #for new add
    def add_contact?
      params[:commit] == "Add contact"
    end

    # Never trust parameters from the scary internet, only allow the white list through.
    def supplier_params
      params.require(:supplier).permit(:name, :notes, :min_order, :pay_terms, 
                                       addresses_attributes: [:id, :name, :street1, :street2, :county, :city, :country, :post_code, :_destroy,
                                       contacts_attributes:[:id, :name, :job_title, :tel_no, :fax_no, :mobile_no, :email, :invoice_email, :contact_notes, :_destroy]])
    end

end

Form:

<%= form_for(@supplier) do |f| %>

      <%= f.label :name %>
      <%= f.text_field :name %>

  <%= f.fields_for :addresses do |address| %>

    <%= address.label :street %>
    <%= address.text_field :street %>

    <%= address.fields_for :contacts do |contact| %>

    <%= contact.label :job_title %>
    <%= contact.text_field :job_title %>

    <%end%>

    <%=hidden_field_tag params[:add_id], address.index%>
    <div class="row">
      <div class="actions text-center">
        <%= f.submit  "Add contact",:class => 'btn btn-sm btn-warning custom2' %>
      </div>
    </div>

  <%end%>

  <div class="row">
      <div class="actions text-center">
        <%= f.submit "Add address",:class => 'btn btn-sm btn-warning custom2' %>
        <%= f.submit :class => 'btn btn-sm btn-success custom2' %>
      </div>
    </div>

<%end%>
Patrishio
  • 1
  • 1
  • Just in case, do you use accepts_nested_attributes_for on the parent model so to be able to use "fields.for" builder for child object attributes? Check [here](http://api.rubyonrails.org/classes/ActiveRecord/NestedAttributes/ClassMethods.html#method-i-accepts_nested_attributes_for). Also [this](http://stackoverflow.com/questions/17558309/accepts-nested-attributes-for-what-am-i-doing-wrong) questions – Pavel Bulanov May 30 '16 at 10:06
  • Also, "fields_for" specifically does not create a new form (only form_for does), and there are no nested forms allowed. So you technically can't have "local" submit buttons inside the form for its portions. You can either have independent forms (several subsequent form_for), or one general submit for whole form. – Pavel Bulanov May 30 '16 at 10:10
  • I have nested attributes added in my model, I've updated question. I want everythig in one big form, everything is working now despite this adding contacts to proper addres, becouse I cannot send address param with submit button to supplier controller. – Patrishio May 30 '16 at 10:33
  • Ok, Would you please describe the scenario and resulted output (error or something) as of now? Just in case, index attribute of fields_for builder is really an index (1, 2, 3 - counter), _not_ and object id <%=hidden_field_tag params[:add_id], address.index%>. Also, .addresses attribute of supplier is not really an array. Try using @supplier.addresses.all[index] and see if it changes anything – Pavel Bulanov May 30 '16 at 10:52
  • Seems to understand your question a bit more. As a streamline solution, you can use "indexed" attribute of submit button, e.g. name attribute. Like <%= f.submit "Add contact", name: "add-contact-#{address.index}", and use name as a differentiation. See [here](http://apidock.com/rails/ActionView/Helpers/FormBuilder/submit#986-About-the-options-argument) – Pavel Bulanov May 30 '16 at 10:58
  • [this](http://stackoverflow.com/questions/3027149/how-do-i-create-multiple-submit-buttons-for-the-same-form-in-rails) may help – Pavel Bulanov May 30 '16 at 11:05
  • I've changed hidden_field_tag params[:add_id], address.index to hidden_field_tag :add_id, (address.index-1). Problem is that it send always the last address index [for example: i click the submit button [add contact] belongs to first address and index should be 0 but it is the last index = 1]. I have to find the way to send in parameter index belongs addres to which I want to add new contact. – Patrishio May 30 '16 at 11:29
  • I've tried to send manualy params hidden_field_tag :add_id, '0' hidden_field_tag :add_id, '1' and @supplier.addresses[params[:add_id].to_i].contacts.build works correctly for my – Patrishio May 30 '16 at 11:32
  • Thank you @PavelBulanov. That was what i looking for. I need now to adapt to my controller to this approach becouse submit button behavior is depend of submit name - and now name differ. – Patrishio May 30 '16 at 12:03

0 Answers0