Question

My end goal is to be able to add costumes to an agreement in the agreement view, regarless of whether or not they exist in the Costume database yet. My main difficulty is that a costume does not belong to an agreement, they exist independently but can be added to an agreement. If a new costume is added that isn't in the Costume database, it will add it to the database. Is there a way to do this? I can't find a tutorial about this anywhere. If I could get the controller from this post, I think that is all I need. I just need to create one costume every time the form is displayed. Thanks so much.

My models are as follows:

# app/models/agreement.rb
class Agreement < ActiveRecord::Base
  has_and_belongs_to_many :costumes, join_table: :agreement_costumes
  accepts_nested_attributes_for :costumes, :reject_if => :all_blank
end

# app/models/costume.rb
class Costume < ActiveRecord::Base
  has_and_belongs_to_many :agreements, join_table: :agreement_costumes
end

# app/models/agreement_costume.rb
class AgreementCostume < ActiveRecord::Base
  belongs_to :agreement
  belongs_to :costume
end

My agreement controller is as follows:

class AgreementsController < ApplicationController
  before_action :set_agreement, only: [:show, :edit, :update, :destroy]

  # Some methods ommitted

  # GET /agreements/new
  def new
    @agreement = Agreement.new
    @agreement.costumes.build
    @costumes = Costume.all
  end

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

    # Never trust parameters from the scary internet, only allow the white list through.
    def agreement_params
      params.require(:agreement).permit(:name, :phone, :email, :mailbox, :wesid, :title, :start, :end, :due, :financer, :employee, :costumes_attributes => [:cid, :description, :wd, :back, :photo])
    end
:end

And finally, the agreement view

<%= form_for(@agreement) do |f| %>
  <% if @agreement.errors.any? %>
    <div id="error_explanation">
      <h2><%= pluralize(@agreement.errors.count, "error") %> prohibited this agreement from being saved:</h2>

      <ul>
      <% @agreement.errors.full_messages.each do |msg| %>
        <li><%= msg %></li>
      <% end %>
      </ul>
    </div>
  <% end %>

  <!-- Agreement fields omitted -->

  <%= f.fields_for :costumes do |c| %>
    <div class="field">
      <%= c.label :cid %><br>
      <%= c.number_field :cid %>
    </div>
    <div class="field">
      <%= c.label :description %><br>
      <%= c.text_field :description %>
    </div>
    <!-- etc with costume fields -->
  <% end %>
  <div class="actions">
    <%= f.submit %>
  </div>
<% end %>
Was it helpful?

Solution

You need accepts_nested_attributes_for in your Agreement controller if you want to create new costumes there.

class Agreement < ActiveRecord::Base
  has_many :agreement_costumes
  has_many :costumes, through: :agreement_costumes

  accepts_nested_attributes_for :costumes, :reject_if => :all_blank, :allow_destroy => :false,
end

and then in the agreements#new action in your Agreements controller you build a costume entry

def new
  @agreement = Agreement.new
  @agreement.costumes.build
  @costumes = Costumes.all
end

@agreement.costumes.build creates a blank instance of a costume, related to this agreement. You then access the params of that costume in the form using :costumes Don't forget to whitelist your nested params in your Agreements controller:

def agreement_params
  params.require(:agreement).permit(:name, :phone, :email, :mailbox, :wesid,
   :title, :start, :end, :due, :financer, :employee, costumes_attributes[:name, :price, :id])
end

Now your form has to have a place to choose existing costumes from a list and/or add a new one.

<%= f.label :costumes, "Costumes" %>
<%= f.collection_select :costumes, :agreement_id, :id, :name, price, {}, {multiple: true} %>

<strong>Add a new costume</strong>
<%= f.fields_for :costumes do |c|
  <%= c.label :name %>
  <%= c.text_field :name %>
    <br>
  <%= c.label :price %>
  <%= c.number_field :price %>
    <br>
<% end %>

This should get you most of the way there. I've had to guess at some of your code, so this isn't going to be a cut and paste answer. You'll need to build what you can off of this and probably do a little more Googling here and there. If you wanted to have a popup form to add a costume to the list on the fly and then be able to choose it in the collection_select, you would have to turn on Turbo_links in your app, create a Javascript popup form. Then use AJAX to submit the form, save the costume to the database, run another .js.erb file that would then update the collection_select text list using a reload of just that list via your Javascript. It's actually probably easier than having a new costume form nested in this form.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top