23

I have a React client app that needs to talk to a Rails API. I want to use the rails-ujs method Rails.ajax. For example:

Rails.ajax({
  type: "POST", 
  url: "/things",
  data: mydata,
  success: function(response) {...},
  error: function(response) {...}
})

It looks like I can't set data to a JSON object like this:

mydata = {
 thing: {
  field1: value1,
  field2: value2,
}}

I need to convert it to a application/x-www-form-urlencoded content type manually like this:

mydata = 'thing[field1]=value1&thing[field2]=value2'

This is ok for flat data but gets complicated quickly for nested data.

jQuery does the conversion automatically before making a request.

So I'm wondering if Rails UJS has some automatic way of doing it, but I couldn't find anything in the docs or code.

Matilda Smeds
  • 1,384
  • 11
  • 18
Hrishi Mittal
  • 1,455
  • 10
  • 15
  • 1
    If you are trying to send form elements then `Rails.serializeElement` might help: [rails-ujs/utils/form.coffee](https://github.com/rails/rails/blob/master/actionview/app/assets/javascripts/rails-ujs/utils/form.coffee) – Marklar Oct 04 '18 at 03:11
  • I got to your post https://learnetto.com/blog/rails-ajax and here too, and I was wondering if you found any more readable solution than formating them manyally? – alexventuraio Jan 27 '21 at 17:53

7 Answers7

16

When using Rails UJS, data must be formatted as string form parameters. Unfortunately, it's not possible to provide JSON, at least not currently with Rails 6.0.2 (not ideal).

Solution

Use URLSearchParams to convert JSON to URL paramaters:

myData = {
 thing: {
  field1: value1,
  field2: value2,
}}

Rails.ajax({
  type: "POST",
  url: "/things",
  data: new URLSearchParams(myData).toString()
})
Jeremy Lynch
  • 6,780
  • 3
  • 52
  • 63
9

Here is my workaround to put/post with Content-Type: application/json.

It works by setting options.data in the beforeSend callback. This way the internal Rails.ajax.createXHR method does not set the Content-Type header first. https://github.com/rails/rails/blob/master/actionview/app/assets/javascripts/rails-ujs/utils/ajax.coffee#L53

    Rails.ajax({
      url: 'http://some-url.com',
      type: 'put',
      beforeSend(xhr, options) {
        xhr.setRequestHeader('Content-Type', 'application/json; charset=UTF-8')
        // Workaround: add options.data late to avoid Content-Type header to already being set in stone
        // https://github.com/rails/rails/blob/master/actionview/app/assets/javascripts/rails-ujs/utils/ajax.coffee#L53
        options.data = JSON.stringify(data)
        return true
      },
    });
Freddy
  • 402
  • 4
  • 8
  • Had problems with trying to move away from jquery and javascript assets pipeline (move to webpacker) on a new rails 6 application. This was the only one that worked. My old jquery code just used a Get query and couldn't get that to work.Moved to a Post and this worked fine. It was even in a stimulus controller – appleII717 Dec 10 '19 at 22:08
  • holy **, that's still valid for 2022. Couldn't find any other solution to send ajax with rails-ujs – zhisme May 19 '22 at 14:43
7

I used ajax call several times and I used to send json data like this.

var fd = new FormData();
fd.append("jsondata", JSON.stringify(mydata));

$.ajax({
  url: ...
  type :"post",
  data: fd
  success:function(){
  }
})

Parsing Json data in ruby controller is easy. you can just use JSON.parse(params["jsondata"])
Hope this works for your case.

artgb
  • 3,177
  • 6
  • 19
  • 36
1

I reviewed the library code and It doesn't support that because in order to send an object you have to stringify the object, and after doing that I discovered that the library checks if the data is the type of string It changes the content type of the request to application/x-www-form-urlencoded . This link shows that condition in the library. And for that, your option to overcome this issue is to write a method that transforms the object to input form way or use JQuery Ajax or another library.

Ahmed Magdy
  • 191
  • 4
  • Yes, you're right, Ahmed. That's the bit of code I looked at too. I just wondered if there was another place where it pre-processes the data in case I had missed it. Thanks for your answer. – Hrishi Mittal Aug 23 '17 at 12:07
0

Not sure if I'm misunderstanding the issue, but can't you just convert your object using JSON.stringify?

https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify

Certainly I can see in the rails-ujs module it's using JSON.parse to parse the Json response

DJ Forth
  • 1,518
  • 2
  • 18
  • 26
  • 1
    You're right, I can convert it myself. My question was if there's an inbuilt way in rails-ujs (like in jQuery, see [processData](http://api.jquery.com/jquery.ajax/)). Also, I'm talking about JSON in the request data, not response. – Hrishi Mittal Aug 25 '17 at 08:09
  • I don't think it does, but not sure why that's a massive deal? It's pretty low overhead to JSON.stringify. Or am I not understanding what your trying to do? – DJ Forth Aug 25 '17 at 16:52
0

I noticed no actual solution for the stated problem, just workarounds.

Here is a link to a gist (JS class) that will handle the required serialization:

https://gist.github.com/dansimpson/231546

Vlad
  • 3,866
  • 1
  • 24
  • 20
-2

you can set the content type in your ajax call and sending the data in json format

var data={
      user:{
        first_name: firstname,
        last_name: lastname,
        username: username
      }}

send the ajax call by placing content type as json in http header request

$.ajax({
   type: "POST",
   url: "http://someurl/post",
   data:data,
   contentType: "application/json",
   success: function(data) {
       -------
  }
});​