I saw this code in a Rails controller:
respond_to do |format|
format.js {}
end
I've seen this for XML and HTML formats but not for Javascript.
Is this the way you specify a return format if you use for REST, like if you use replace_html or remote_form_for? I know RJS templates return compiled Javascript so I'm thinking maybe this is where this code might kick in.
If you put code inside the hash symbols(format.js {}), is that what gets send back as javascript to the browser?
It is used when an AJAX request is sent from the browser to a controller. The controller can respond with a script (which is generated by ruby statements in the view) which will be executed on the client.
Rails does a little magic on figuring out what 'template' to send out
in controller:
def foo
end
in view: (app/views/controller/) you can have
foo.html.erb (usual, html template)
foo.rjs (javascript template)
rails will send out the right template back to the browser, HTML for regular requets and RSJ for Ajax requests. You might want to put in javascript code like 'page.replace_html' ..etc in your RJS template. This way, you keep the controller clear of view code.
yuo can always just add the format to the url and see what it responds, /something.js would respond using the format.js code, if you want to use it, you can do the following to avoid rendering your entire layout:
format.js { render :layout => false, :text => #models.to_json }
that would respond with a json string
format.js { render :layout => false }
would require a template called [action].js.erb
Related
I have Vacancies controller and I need to pass #vacancies to json and also render another layout. The following code does not work (json is not passed however I have "wide" layout). If I remove format.html { render layout: "wide"} } json passes correctly. How to combine these two things?
class VacanciesController < ApplicationController
respond_to :html, :json
...
def index
#vacancies = Vacancy.all
respond_with(#vacancies) do |format|
format.html { render layout: "wide"} }
format.json { render json: #vacancies }
end
end
...
You can't call render twice, that's problem #1. You also can't send two responses to a single request.
There is also no purpose in rendering HTML (which means a fresh page load) and sending JSON (which is for AJAX requests, ie requests that don't reload the page) at the same time. It isn't possible, but it also would be pointless even if it was possible.
If you want to tell a request to use a specific layout, you can pass the layout option to a render call. However, a render call does not take a data object as the first argument, it takes a view name, or only an options hash. So to call this correctly you should use:
render :index, :layout => 'example'
I expect that will make your HTML views show up correctly.
Please understand however, the layout option is only useful for HTML responses, not JSON responses. Layout is telling your render call what outer HTML to wrap around the view your action is calling, and if you don't specify it uses 'application.html'
To help you understand one more thing: your respond block is telling the computer how to respond to different kinds of requests. It's like a switchboard. If you wrote it with if/else statements it might look like this:
if request_type == 'html'
render :index, :layout => 'wide'
elsif request_type == 'json'
render :json => #vacancies
else
raise raise ActionController::UnknownFormat
end
So, with your respond_with block, if you fix your html render call, and assuming you're developing on localhost, if you enter the following URL in your browser and hit enter...
http://localhost:3000/vacancies
That would be making an HTML format GET request, which will load the page with layout: 'wide' but no other data. If you type:
http://localhost:3000/vacancies.json
That will simulate a JSON request, and you'll get just the JSON representation of the #vacancies data.
I hope that helps you solve your problem. If not, please describe what you're trying to accomplish in more detail so I can help you understand how to do it.
PS: one last tip: if you want to specify layouts at the controller level you can just call layout at the top of your controller, like so:
class ExampleController < ApplicationController
layout 'awesome', :only => [:new,:edit]
...
end
This works like any other filter, you can pass :only, or :except, or no options at all.
I would like to use my form partial for html and js requests. For the second i have to put :remote => true into form_for tag.
Is it possible to read the response type (html, js, ...) and use this as condition in a view?
For instance, this example of respond_to's usage should help you. And for complement: There is a Discussion in Ruby Forum you can take advantage.
respond_to do |format|
format.js { #Magic goes here }
You can also check before the respond_to block by calling request.xhr?. But be aware with the respond_to block. AJAX can respond with html (i.e $.ajax({dataType: 'html'}))
I need to send some html code to another site via API on my rails 2.3 application.
The html code is rendered by another action.
Is there a good way for a controller to get the rendered view html code?
One way I can think of is that action A calls action B via HTTP and get the result.
However, I think this is a waste of resources.
Is there a better way?
Thanks.
Sam
I think you want render_to_string which will send the output of the render to a string, instead of back to the client. Use it in your format.js response block.
See api doc
respond_to do |format|
format.js { render(:partial => 'some_thing.html.erb', :layout => false) }
end
I have a rails action called index that renders the content for my page along with the layout. When I go to the /index action with a browser it works like expected. I want to be able to also render this action by calling it with Ajax, I am doing this using the following:
<%= link_to "Back", orders_path, :id => 'back_btn', :remote => true %>
<%= javascript_tag do %>
jQuery("#back_btn").bind("ajax:complete", function(et, e){
jQuery("#mybox").html(e.responseText);
});
<% end %>
When the action is called this way I would like it to render and pass the index action back, excluding the layout. How can I do this?
You should be able to add a format.js action to your controller action like so:
respond_to do |format|
format.js
format.html
format.json { render json: #foos }
Ideally, you would want to create a index.js.erb file that would build the contents of the page:
$('#foos_list').update("<%= escape_javascript(render(#foos)) %>");
If you're going to update the contents of a div, to basically update a whole page inside of a layout, then you're going to want to change it up a little bit. Inside of the format.js, you can do this:
format.js { render 'foos/index', :layout => false }
But if you're trying to go with an ajaxified front-end, may I recommend a framework for doing this, like Spine? It will go a long way in helping you build your site.
Also, using a framework like this will force you to separate your application per #Zepplock's second suggestion.
You can just detect if the request is an XML HTTP Request, then render a blank layout like so:
render layout: 'blank' if request.xhr?
You'll need to create a blank layout in app/views/layouts/blank.html.erb like this:
<%= yield %>
You need a way to let server know that there's a difference in request type. It can be done in several different ways:
Append a key value to the URL (for example layout=off) and change your controller logic to render data with no view. This is kind of a hack.
Make your controller return data via XML or JSON (controller will know what content type is being requested) then format it accordingly and present in browser. This is more preferred way since you have a clear separation between content types and is better suited for MVC architecture.
Create an API that will serve data. This will lead to separate auth logic, more code on client side, additional APi controller(s) on server etc. Most likely an overkill for your case
I'm running the latest Rails 2-3-stable branch (currently 2.3.3).
I'm using JQuery to post an AJAX request to my 'create' action, in which I have the following block:
respond_to do |format|
format.js
end
I have created create.js.erb and to test this action, I've added the following single line:
alert('hello');
The request correctly enters the format.js block, but the response attempts to render a layout. Here's my log:
Jul 22 20:44:27 [2970] INFO: Rendering template within layouts/application
Jul 22 20:44:27 [2970] INFO: Rendering contacts/create
If I change my respond_to block to the following, it works:
respond_to do |format|
format.js { render :layout => false }
end
Is this expected behaviour or is this a bug in Rails? I would have thought the fact that I'm rendering a JS response would be enough to set layout to false.
I use this:
class ApplicationController < ActionController::Base
# this is needed to prevent XHR request form using layouts
before_filter proc { |controller| (controller.action_has_layout = false) if controller.request.xhr? }
It works like charm. You have to put this onliner only in one place and noting more.
I have spend ~1 h to write this line.
The best way I've found to handle this is to add a file like this:
app/views/layouts/application.js.erb
<%= yield -%>
If you don't include the yield, your js actions will all fail, since the app will never render the view. This technique solves the layout problem and also offers the ability to add universal js code at the layout level, such as flash processing or triggering jQuery UI onLoad hooks.
My memory of Ajax on Rails books is that this was the standard if not necessarily expected behaviour in earlier editions of rails.
The following ticket shows the bug logged a few versions back, as well as a way to define the behaviour as default.
http://dev.rubyonrails.org/ticket/3229
Sadly the description of what the later changes are that make this obsolete are not explained in the final comment.
The JQuery function which makes the Ajax POST request should return "false". Check if you are doing the same. It should be implemented like following:
postFormData: function () {
$('#form_id').submit(function () {
$.post($(this).attr('action'), $(this).serialize(), null, 'script');
return false;
});
}
Notice the last line which returns false.