JBellah
JBellah

Reputation: 31

Rails 3 issue with best_in_place and Nested Attributes

I'm a bit of a Rails noob and I'm having some trouble getting my head around an issue.

I'm using Rails 3.2.13 with the following gems (in addition to the default gems):

gem 'devise'
gem 'cancan'
gem 'cocoon'
gem 'best_in_place'

I am using cocoon to work with nested models, in this case I have a (devise) user who has_many projects and each project has_many tasks.

I am able to get all of the information to display (and become editable on click) with:

<% @project.tasks.each do |task| %>
<%= best_in_place task, :description, :path => tasks_path, :type => :input %>
<% end %>

My problem is that I cannot get best_in_place to save updates to my nested Task attributes.

Project Model:

class Project < ActiveRecord::Base
  belongs_to :user

  has_many :tasks

  attr_accessible :description, :name, :tasks_attributes, :user_id, :tasks
  accepts_nested_attributes_for :tasks, :reject_if => :all_blank, :allow_destroy => true
end

Task Model:

class Task < ActiveRecord::Base
  belongs_to :project

  attr_accessible :description, :done, :hours
end

Project Controller:

class ProjectsController < ApplicationController
  before_filter :authenticate_user!

  def show
    @project = Project.find(params[:id])

    respond_to do |format|
      format.html # show.html.erb
      format.json { render :json => @project }
    end
  end

def update
  @project = Project.find(params[:id])

    respond_to do |format|
      if @project.update_attributes(params[:project])
        format.html { redirect_to @project, :notice => 'Project was successfully updated.' }
        #format.json { head :no_content }
        format.json { respond_with_bip(@project) }
      else
        format.html { render :action => "edit" }
        #format.json { render :json => @project.errors, :status => :unprocessable_entity }
        format.json { respond_with_bip(@project) }
      end
    end
  end
end

Projects -> show.html.erb:

<% @project.tasks.each do |task| %>
  <li class="module-list-item ui-state-default clear">
    <section class="task-name left">
      <%= best_in_place task, :description, :path => tasks_path, :type => :input %>
    </section>
  </li>
<% end %>

Upvotes: 3

Views: 1009

Answers (2)

Isaac Betesh
Isaac Betesh

Reputation: 3000

Though this use is not officially supported (as @Ich points out), there is a workaround. No need for any additional controllers. You just need to pass a param param to best_in_place.

class Unicycle < AR::Base
  has_one :wheel
  accepts_nested_attributes_for :wheel, update_only: true
end

best_in_place unicycle.wheel, :last_rotation, 
  path: unicycle_path(unicycle.id), type: :input, 
  param: "unicycle[wheel_attributes]"

Note that it's slightly more complicated for a Car, which has_many :wheels (or for a Unicycle that has_one :wheel where update_only won't work for you).

car.wheels.each do |wheel|
  best_in_place wheel, :last_rotation, 
    path: car_path(car.id), type: :input, 
    param: "car[wheel_attributes][id]=#{wheel.id}&car[wheel_attributes]" 
    # Note that you have to pass the ID of the wheel in this case
end

Works with v3.0.3, possibly with much earlier versions too but I haven't tested.

Upvotes: 2

Ich
Ich

Reputation: 1378

According to https://github.com/bernat/best_in_place/issues/51 this is not supported by best_in_place.

The gem's author argues that in place editing should only modify on attribute of one model, so changing another model's attributes was not in scope.

My opinion: Personally I regret that decision.

Upvotes: 0

Related Questions