Ryan
Ryan

Reputation: 11706

Rails 5 jQuery .ajax method data format

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

Answers (3)

max
max

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

Ryan
Ryan

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

Roman Kiselenko
Roman Kiselenko

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

Related Questions