2

I've been looking for similar topics but although I've read a lot I'm still a bit confused about the respond_to block usage.

I'm doing an AJAX request by using form_with in the client side. In the controller, my action looks like this:

  def create
    @role = Role.new(role_params)

    respond_to do |format|
      if @role.save
        format.html { redirect_to url_for(controller: 'roles', action: 'index') }
        format.json { render json: { :location => url_for(controller: 'roles', action: 'index') }, status: 302 }
      else
        format.html { render action: 'new' }
        format.json { render json: { :errors => @role.errors }, status: 422 }
      end
    end
  end

The way I understand respond_to bock is that when you make an AJAX request it should answer back by using json, and If you make a regular request it should answer back by using html. Is that correct?

In this case, it always answers back by using format.html. I've checked that If I put format.json first (above format.html) It indeed answer back by using json.

What's wrong or what am I missing?

Thanks!

4
  • 1
    how do you call form_with from the front end ? Commented May 7, 2020 at 0:48
  • <%= form_with scope: :role, url: admin_roles_path, html: { class: 'form-container needs-validation', novalidate: 'novalidate', id: 'new_role' } do |f| %> Commented May 7, 2020 at 1:01
  • 1
    to get the format you want try to use the path as follows: for json try : admin_roles_path(format: :json) for html try: admin_roles_path(format: :html) does this work for you ? Commented May 7, 2020 at 1:16
  • That's correct. Thank for your answer. If I specify the format it returns properly. Anyway, using RoR helpers to make AJAX requests the predefined format is 'js', is it correct? Commented May 7, 2020 at 1:24

2 Answers 2

1

The way I understand respond_to bock is that when you make an AJAX request it should answer back by using json, and If you make a regular request it should answer back by using html. Is that correct?

Not quite. An AJAX request is just an asynchronous request and the response type should depend on the Content-Type and Accept-Type headers. An AJAX request request can actually request any possible content type - JSON is just the most commonly used and arguably the most useful type.

If the request does not contain a specific content type or accept type Rails will default to html unless you override it in the routes:

namespace :api, defaults: { format: :json } do
  namespace :v1 do
    resources :things
  end
end

Rails UJS which is built into rails and powers the remote: true option on forms and links actually uses application/javascript as the default content-type as it lets you write js.erb views and reuse rails templating without writing ajax handlers. If this is really a good idea though is debatable as it leads to some very questionable design decisions.

With Rails UJS the easiest way to set the content type is through the data-type attribute:

<%= link_to "Click Me!", "/foo", remote: true, data: { type: :json } %>
<%= form_with(model, html: { data: { type: "json" }}) %>

If you are sending an Ajax request "manually" with XMLHttpRequest you can set the content type with setRequestHeader.

xhr.setRequestHeader("Content-Type", "application/json"); 

With jQuery you use the type: option for the ajax funtions or jQuery.getJSON.

Also the correct way to respond is to a successful POST request is with 201 Created.

201 Created. The request has been fulfilled and has resulted in one or more new resources being created. The primary resource created by the request is identified by either a Location header field in the response or, if no Location field is received, by the effective request URI.

format.json { head :created, location: @role }

You can also optionally include the created resource in the response body.

format.json { render json: @role, location: @role, status: :created }
Sign up to request clarification or add additional context in comments.

3 Comments

On a side note AJAX actually stands for Asynchronous Javascript and XML. JSON did not even exist when Jesse James Garrett coined the term and it was though back then that XML would be the defacto exchange format for the internet.
You're very right to be confused - Rails UJS is a mess. The way rails ujs was intended to be used was with a bunch of spaggetti code js.erb views that alter the DOM when the are run by evaluating the response. If you don't want to get into that shitshow you can use a json content type and write handlers for the ajax:complete event. guides.rubyonrails.org/…
Yep, I read it. I'm using Stimulus to try to avoid that JS spaghetti code. I think they (dhh and the rest of RoR team) should focus in improving the JS documentation (at least with RoR 6) and explaining a better way to accomplish some basic tasks when using JS (AJAX requests for instance).
1

form_with by default sends XHR (ajax) request requiring you to have a java-script template for the response and then use format.js inside the respond method.

however if you want to make a json or any other request formats such as xml you can pass the format as a parameter to the path helper method like this:

admin_roles_path(format: :json)

or use the format option directly in the form_with call but since you are already using the url option the format option will be omitted.

for more information this is a good post to read.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.