Reputation: 11706
After upgrading to Rails 5, I am having trouble with my jQuery .ajax requests. I believe this has to do with the (somewhat) new strong parameters. I receive the following error:
ArgumentError (When assigning attributes, you must pass a hash as an argument.):
I am thinking that I am setting up my data package incorrectly in my .ajax request (using Coffeescript).
Or possibly that my trusted parameters are not working correctly.
Using byebug, it appears the first line of my controller is okay, but that it fails on the second line: @prospect.update(prospect_params[:status])
. Here is the @_params variable in byebug:
1: @_params = <ActionController::Parameters {"prospect"=><ActionController::Parameters {"id"=>"16", "status"=>"inactive"} permitted: false>, "controller"=>"prospects", "action"=>"update_status"} permitted: false>
Here are snippets from my controller, and Coffescript
Controller
# PUT /prospects/update_status
def update_status
@prospect = Prospect.find(params[:id])
@prospect.update(prospect_params[:status])
respond_to do |format|
if @prospect.update_attributes(prospect_params)
format.json { head :no_content }
else
format.json { render json: @prospect.errors, status: :unprocessable_entity }
end
end
end
...
private
# Use callbacks to share common setup or constraints between actions.
def set_prospect
@prospect = Prospect.find(params[:id])
end
# Only allow a trusted parameter "white list" through.
def prospect_params
params.require(:prospect).permit(:name, :status, :priority, :id)
end
Coffeescript
# Update prospects active list when a toggle switch is clicked
$("#prospects_list").on "change", ".js-switch", ->
prospect_id = $(this).parent('form').find('input[name="prospect[id]"]').val()
if @checked
status = 'active'
else
prospect_status = 'inactive'
# Get parent TR
tr = $(this).closest('tr')
# Update prospect using Ajax
$.ajax
url: '/prospects/update_status'
type: 'POST'
data: { prospect: {id: prospect_id, status: prospect_status } }
dataType: 'json'
success: (data) ->
return
false
Upvotes: 1
Views: 941
Reputation: 102045
Start by asking yourself if you really need a specific route for updating the status or if this should be a request to the canonical update route:
PATCH /prospects/:id
In that case you should change your AJAX handler:
# Update prospects active list when a toggle switch is clicked
$("#prospects_list").on "change", ".js-switch", ->
prospect_id = $(this).parent('form').find('input[name="prospect[id]"]').val()
if @checked
status = 'active'
else
prospect_status = 'inactive'
# Get parent TR
tr = $(this).closest('tr')
# Update prospect using Ajax
$.ajax
url: '/prospects/' + prospect_id
type: 'PATCH' # Not POST!
data: { prospect: { status: prospect_status } }
dataType: 'json'
success: (data) ->
return
false
If you for some reason want a specific method for this you can fix it by moving the :id to the path instead of the params.
# config/routes.rb
resources :prospects do
member do
patch :update_status
end
end
# PATCH /prospects/:id/update_status
def update_status
@prospect = Prospect.find(params[:id])
respond_to do |format|
if @prospect.update_attributes(prospect_params)
format.json { head :no_content }
else
format.json { render json: @prospect.errors, status: :unprocessable_entity }
end
end
end
# ...
def prospect_params
params.require(:prospect).permit(:name, :status, :priority)
end
# coffeescript
url: '/prospects/' + prospect_id +'/update_status'
Using @prospect.update(prospect_params[:status])
was updating the record twice and also skirts the strong parameters so it will raise an exception.
Upvotes: 0
Reputation: 11706
Thanks to Зелёный and max for getting me in the right direction. Here is what I used that worked. I only needed a small change to the controller
Controller
# PUT /prospects/update_status
def update_status
@prospect = Prospect.find(prospect_params[:id])
respond_to do |format|
if @prospect.update(status: prospect_params[:status])
format.json { head :no_content }
else
format.json { render json: @prospect.errors, status: :unprocessable_entity }
end
end
end
Upvotes: 0
Reputation: 44370
You pass the value of your parameters.
Look at the error message you must pass a hash as an argument.
@prospect.update(prospect_params[:status])
# here is you pass a value of status key, that's not a hash
# {"id"=>"16", "status"=>"inactive"}
# prospect_params[:status] => "inactive"
Use just:
@prospect.update(prospect_params)
# that's pass a hash
# prospect_params => {"id"=>"16", "status"=>"inactive"}
Or:
@prospect.update(status: prospect_params[:status])
Upvotes: 3