How can can.js add a record to a Rails server?

别来无恙 提交于 2019-12-07 02:30:29

As shown in the Rails console output, your params Hash looks like the following:

Parameters: {"name"=>"mow lawn"}

But your controller is creating a new Todo instance using the following:

@todo = Todo.new(params[:todo])

:todo is not in the params Hash, so params[:todo] is nil and basically your Todo instance has no attributes passed to it, hence it is saving nil for the name column in the DB.

Your controller should be using parameter wrapping to solve this, which will wrap all the params Hash into a params[:todo] sub-Hash. So if you have this in your controller:

class TodosController < ApplicationController
  wrap_parameters :todo
  ...
end

Your params Hash will be transformed to the following before the action is performed:

Parameters: {"name"=>"mow lawn", "todo" => {"name"=>"mow lawn"}}

And then your existing action code should work.

Like Stuart M said, you need to set the wrap_parameters :todo in the controller.

But wrap_parameters :todo does not work with the default Content-Type: application/x-www-form-urlencoded canjs sends on it's model updates.

Here are the resources that let me work it out:

https://github.com/bitovi/canjs/issues/111

https://forum.javascriptmvc.com/topic/howto-use-application-json-and-encode-data-automatically-in-can-model

So by reading those I changed my app to look like this:

app/controllers/todo_controller.rb

class TodosController < ApplicationController
  wrap_parameters :todo

  def create
    Todo.new(params[:todo])
  end

end

app/assets/javascripts/canjs.models.js

$.ajaxPrefilter(function(options, originalOptions, jqXHR){
    if( options.processData && /^(post|put|patch|delete)$/i.test( options.type ))
    {
        options["contentType"] = "application/json";
        options["data"] = JSON.stringify(originalOptions["data"]);
    }
});

Todo = Model.new({
    ...
});

And everything worked like magic :)


NOTE: You may need to setup CORS for this to work, here is how to set it up on a public api:

config/routes.rb

SomeTodoApp::Application.routes.draw do

  resources :todos, except: [:new, :edit]

  match "*all" => "application#cors_preflight_check", via: :options

end

app/controllers/application_controller.rb

class ApplicationController < ActionController::API

# protect_from_forgery is always a good thing if you know your clients can support get it working

  protect_from_forgery

  before_filter :cors_preflight_check
  after_filter :cors_set_access_control_headers

# For all responses in this controller, return the CORS access control headers.

  def cors_set_access_control_headers
    headers['Access-Control-Allow-Origin'] = '*'
    headers['Access-Control-Allow-Methods'] = 'POST, PUT, PATCH, GET, OPTIONS'
    headers['Access-Control-Allow-Headers'] = 'Origin, X-Requested-With, Content-Type, Accept, Authorization'
    headers['Access-Control-Request-Method'] = '*'
    headers['Access-Control-Max-Age'] = "1728000"
  end

# If this is a preflight OPTIONS request, then short-circuit the
# request, return only the necessary headers and return an empty
# text/plain.

  def cors_preflight_check
    if request.method == :options
      headers['Access-Control-Allow-Origin'] = '*'
      headers['Access-Control-Allow-Methods'] = 'POST, PUT, PATCH, GET, OPTIONS'
      headers['Access-Control-Allow-Headers'] = 'Origin, X-Requested-With, Content-Type, Accept, Authorization'
      headers['Access-Control-Request-Method'] = '*'
      headers['Access-Control-Max-Age'] = '1728000'
      render :text => '', :content_type => 'text/plain'
    end
  end

end
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!