I am currently trying to use the best_in_place gem in order to do inline editing within an HTML table. I am showing a cart in cart's show view. Within the cart's show view, I have the ability to add lineItems. When an LineItem is created, a new Available record is also created with a lineItem_id and then it's shown in the cart with its lineitem. Both the Cart and LineItem tables come from an external database and because of that, I can't add columns to so that is why I can't just add an available boolean attribute to the LineItem.
**cart.rb
class Cart << AR::Base
has many LineItems
end
**line_item.rb
class LineItems <<AR::Base
belongs_to Cart
has_one :available
accepts_nested_attributes_for :available
end
**available.rb
class Available<<AR::Base
belongs_to LineItems
end
**views/cart/show.html.erb
@cart.lineitems.each do |line_items|
<td><%= line_item.price %></td>
<td><%=line_item.name %></td>
<td><%= best_in_place line_item.available.boolean, :boolean, :path => line_items_path, :type => type: :checkbox, collection: %w[No Yes] %></td>
end
I want to be able to edit the line_item.available.boolean within the html table which is on the cart show view using best_in_place but I am not having any luck.. Any help would be AMAZING! =] I know after reading around that it's not possible using nested attributes, but If I could get rid off the available model somehow and have a field in the show table that I can edit for a line_item to see whether or not the lineItem is available, that would also be great. I am open to any ideas!
Firstly, there are a few syntax issues in your code that we need to fix:
@cart.lineitems.each do |line_item| # changed "line_items" to "line_item"
<td><%= line_item.price %></td>
<td><%=line_item.name %></td>
<td><%= best_in_place line_item.available, :boolean, :path => line_items_path, type: :checkbox, collection: %w[No Yes] %></td> # changed "line_item.available.boolean" to "line_item.available" and ":type => type: :checkbox" to "type: :checkbox"
end
Now, the answer:
As I explain in this Github issue, you need to pass a param
option and a url
option (used to be path
but that was deprecated in v3.0.0) to best_in_place.
The url
option
The default url is the update action of the first argument to best_in_place. Since your code begins with best_in_place line_item.available
, this would default to url_for(line_item.available.id)
. However, you want it to PATCH the update action of the LineItemsController, i.e. url_for(line_item)
The param
option
By default, the param option assumes you are PATCH'ing to the AvailablesController, so here are the parameters that Rails conventions require in order to update available.boolean to the value "1":
{"available"=>{"boolean"=>"1"}}
The ID of the Available is in the URL already, so the only extra param you need to pass is the new value for boolean
.
In your case, however, you are PATCH'ing to the LineItemsController and the available model accepts nested attributes. This requires two adjustments:
The ID of the LineItem is in the URL already, but the ID of the Available is not. We have two choices here: Either put the ID of the Available into the param option, or make the ID unnecessary by passing update_only: true
to accepts_nested_attributes
in the model. The update_only
approach may not work for you depending on your use case, but I find that it is the simplest approach the vast majority of the time and adds an extra layer of security for free.
The boolean option needs to be nested properly, i.e.:
line_items[available_attributes][boolean]
That way, when it gets to the server, the params will be:
{"line_item"=>{"available_attributes"=>{"id"=>"99","boolean"=>"1"}}}
# "99" is just an example of line_item.available.id
Note that you will need to permit these attributes in the controller, i.e.:
line_item.update(params.require(:line_item).permit(
available_attributes: [:id, :boolean]))
# You can remove `:id` if you are using the `update_only` option
Putting it all together, here's your best_in_place method:
best_in_place line_item.available, :boolean,
type: :checkbox,
url: line_item_path(line_item.id),
param: "line_item[available_attributes][id]=#{line_item.available.id}&line_item[available_attributes]"
However, if at all possible, use the update_only
option.
# line_item.rb
accepts_nested_attributes_for :available, update_only: true
Look how much simpler it becomes now:
best_in_place line_item.available, :boolean,
type: :checkbox,
url: line_item_path(line_item.id),
# Note: `url: line_item` might also work.
# If someone can confirm this in a comment, I can update this answer
param: "line_item[available_attributes]"