Rails 4 - nested attributes with Cocoon gem

2019-08-08 18:23发布

问题:

I'm trying to make an app with Rails 4.

I'm using Cocoon gem for nested forms with simple form.

I have a qualifications model, nested in a profile model. The associations are:

Qualifications

  belongs_to :profile

Profile

has_many :qualifications
    accepts_nested_attributes_for :qualifications,  reject_if: :all_blank, allow_destroy: true

In my profile form, I incorporate the qualifications attributes with:

<%= f.simple_fields_for :qualifications do |f| %>

              <%= render 'qualifications/qualification_fields', f: f %>
            <% end %>

In my Qualifications form attributes, I have:

<div class="nested-fields">
<div class="container-fluid">

          <div class="form-inputs">

            <div class="row">
                <div class="col-md-6">
                    <%= f.input :title, :label => "Your award" %> 
                </div>

                <div class="col-md-6">


                </div>


            </div>

            <div class="row">
                <div class="col-md-8">
                    <%= f.input :pending, :label => "Are you currently studying toward this qualification?" %>
                </div>
            </div>       

            <div class="row">
                <div class="col-md-4">
                    <%= f.input :level,   collection: [ "Bachelor's degree", "Master's degree", "Ph.D", "Post Doctoral award"] %>
                </div>




                <div class="col-md-4">
                <%= f.input :year_earned, :label => "When did you graduate?", collection: (Date.today.year - 50)..(Date.today.year + 5) %>
                </div>

          </div>


          <div class="row">
                <div class="col-md-6">
                    <%= link_to_remove_association 'Remove this qualification', f %>
                </div>

          </div>


          </div>

</div>  
</div>      

In my profiles controller I have:

def new
    @profile = Profile.new
    @profile.qualifications_build
    @profile.build_vision
    @profile.build_personality
    @profile.addresses_build
    authorize @profile

  end

  # GET /profiles/1/edit
  def edit
    @profile.build_personality unless @profile.personality
    @profile.qualifications_build unless @profile.qualifications
    @profile.build_vision unless @profile.vision
    @profile.addresses_build unless @profile.addresses
  end

  # POST /profiles
  # POST /profiles.json
  def create
    @profile = Profile.new(profile_params)
    authorize @profile

    respond_to do |format|
      if @profile.save
        format.html { redirect_to @profile }
        format.json { render :show, status: :created, location: @profile }
      else
        format.html { render :new }
        format.json { render json: @profile.errors, status: :unprocessable_entity }
      end
    end
  end

  # PATCH/PUT /profiles/1
  # PATCH/PUT /profiles/1.json
  def update

    # successful = @profile.update(profile_params)

    # Rails.logger.info "xxxxxxxxxxxxx"
    # Rails.logger.info successful.inspect
    # user=@profile.user
    # user.update.avatar
    respond_to do |format|
      if @profile.update(profile_params)
        format.html { redirect_to @profile }
        format.json { render :show, status: :ok, location: @profile }
      else
        format.html { render :edit }
        format.json { render json: @profile.errors, status: :unprocessable_entity }
      end
    end
  end

  # DELETE /profiles/1
  # DELETE /profiles/1.json
  def destroy
    @profile.destroy
    respond_to do |format|
      format.html { redirect_to profiles_url }
      format.json { head :no_content }
    end
  end

  private
    # Use callbacks to share common setup or constraints between actions.
    def set_profile
      @profile = Profile.find(params[:id])
      authorize @profile
    end

    # Never trust parameters from the scary internet, only allow the white list through.
    def profile_params
      params.require(:profile).permit( :title, :hero, :overview, :research_interest, :occupation, :external_profile,
        :working_languages, :tag_list,
          industry_ids: [],
          user_attributes: [:avatar],
          personality_attributes: [:average_day, :fantasy_project, :preferred_style],
          vision_attributes: [ :long_term, :immediate_challenge], 
          qualifications_attributes: [ :level, :title, :year_earned, :pending, :institution, :_destroy],
          addresses_attributes: [:id, :unit, :building, :street_number, :street, :city, :region, :zip, :country, :time_zone, :latitude, :longitude, :_destroy],
          industries_attributes: [:id, :sector, :icon] )
    end
end

My problems are:

  1. when I save all of this and try it, each time I update the profile, it creates a new entry for the qualifications, so the list of qualifications displayed duplicates on every update (even when no update is made to the fields).

  2. When I update the profile form and click remove qualification, it does not get removed from the array. I can delete them in the console, but not using the form.

I'm worried that if I remove

@profile.qualifications_build unless @profile.qualifications

from the edit action in the profile controller, I won't be able to update those attributes.

Also, it doesn't make sense to me that removing that would solve the problem with removing qualifications. Do I need to add something to the remove qualification link to force it to remove the entry?

回答1:

I think you just forgot to add :id in the allowed parameters for qualifications, in your profile_params:

qualifications_attributes: [ :id, :level, :title, :year_earned, :pending, :institution, :_destroy]

Without an ID to match, any qualification will be seen as a new one.