Rails/Ajax/Error Processing - ruby-on-rails

So there are tons of articles about how to do this, but certainly there's a best practice...and I don't know enough to filter out silly solutions, good ones, and best ones.
I simply want to submit my forms via ajax (in a dialog) and get the errors back just like I would without using ajax...meaning I like the rails standard error handeling/flash messages/label classes.
Is the best way to reload the entire partial?
Is the best way to use .js.erb (or coffee) for partial stuff? (If so, can you explain how to use these partials?
Is the best way to parse JSON back into the form somehow?
What else am I missing in my [limited] knowledge base?

The way I'd do it is to render a create.js.erb view like:
$("#my_dialog").replaceWith("<%= j(render 'dialog') %>");
where _dialog.html.erb contains the HTML for the contents of your dialog.
<div id="my_dialog">
<!-- flash stuff etc -->
<%= form_for ... %>
<!-- ... -->
<% end %>
</div>
Your controller, for example, will look something like:
class EntriesController < ApplicationController
def create
#entry = Entry.new(params[:entry])
respond_to do |format|
if #entry.save
format.html { redirect_to #entry }
format.js {} # this will render create.js.erb for js requests
else
format.html { render action: "new" }
format.js {} # this will render create.js.erb for js requests
end
end
end
end
summit like 'dat. If you don't want to reload the whole form you can update or do whatever you want in .js.erb

Using js.erb is the way to go. Here's the rationale:
Reloading part of your page basically defeats the purpose of Ajax - which is to modify the page without having to reload or refresh anything. And parsing JSON would be quite tedious.
Using js.erb lets you easily leverage validations that Rails provides. In your js.erb, you can access anything that you normally would from your controller, including the validation errors, you and you can update DOM elements based on those errors. Since you're modifying individual DOM elements, you don't need to concern yourself over the fact that your form may be inside a partial.

Related

Rails: How to render pages using AJAX and maintain DRY

I have a calendar page that shows a person's routine for an entire month. At the top, I have button for the previous and next months. When those are clicked, I make an AJAX call to the change_date action which updates the dates and finds the new routines. When the change_date.js.erb is called, do I have to write the code to change the content for everyday's actions again? This seems to be violating DRY.
Is there any way I can reuse the partials I used to populate the page on initial load to repopulate the page with the new values?
Of course, there is. Simply use render as you did in your html templates. Usually a setup that supports ajax and regular calls looks something like this (of course names are wildly guessed):
# controller
def change_date
#dates = Date.where(:date => params[:date])
respond_to do |format|
format.html
format.js
end
end
# change_date.html.erb
<ul id="calendar">
<%= render #dates %>
</ul>
# change_date.js.erb
$("#calendar").html("<%= escape_javascript(render #dates) %>");
# _date.html.erb
<li><%= date.date %></li>
So you see, you can simply use the same partial on both scenarios.
And just another suggestion: Depending on your setup it may be more restful to not have another method in the controller, but to use the show method which operates depending on a query param in this case...
I would load the person's routine via ajax when the page is first loaded as well.
I would encapsulate it in a js function and call that function when the page is first loaded and everytime the month is changed.

Is there a way to render a different template based on current user without using conditionals

I have two different layouts, one is completely custom and the other is bootstrap. For admins we want to render a bootstrap view and for non-admins we render it normally. For the most part this is pretty straight forward because admins and users don't share many views -- but there are some.
My original idea involved overriding render so that it would check if there's a bootstrap version of a file. So for example there would be _user.html.erb and _user.bootstrap.html.erb which would have bootstrap specific templating.
I'd like to not modify any controllers so ideally, something like render 'form' would behave smartly and check if there's an _form.bootstrap.html.erb, and if there isn't it would fallback to _form.html.erb
First attempt
My first attempt looked something like this
# I don't think this is the actual method signature of render
def render(options=nil, extra_options, &block)
# if it should render bootstrap and options is a string and there exists a bootstrap version
# set it up to render the bootstrap view
super(options, extra_options, &b)
end
Current attempt
I'm thinking about registering a template that basically checks if a file exists and then uses erb. I haven't made any progress towards this yet.
I figured it out. This is how I did it:
This is set in the application controller, with a before_filter :render_bootstrap
def render_bootstrap
return unless bootstrap?
new_action = "#{self.action_name}.bootstrap"
has_bs_view = template_exists?(new_action,params[:controller],false) || template_exists?(new_action,params[:controller], true)
if has_bs_view
self.action_name = new_action
end
end
I decided to extend this even further so that inside of a view like show.bootstrap.html.erb you can still use render "form" without doing render "form.bootstrap". This was done by overwriting the rails render helper.

Returning: A full page, JSON, and a partial HTML snippet from Ruby on Rails Controller

I know this question has been asked in part a few other times on SO but I was curious about doing it a different way. In my Ruby on Rails app I have an action called list on my UsersController.rb controller. I want this list to respond to 3 different things
The page itself. Rending the whole page of users I specify
A JSON list of users for the page I specify
A partial view of just the rows for the page I'm specifying formatted as HTML.
Imagine a full page (header, footer, everything) with a table that has page 1 of users. When I click page 2 I want to kick off an ajax request back to the same controller action to give me just the html rows for page 2. I also want to persist my JSON API still allowing my controller to return JSON lists when asked. I imagine it looking someting like this.
class UsersController < ApplicationController
def list
respond_to do |format|
format.html # RETURNS MY VIEW
format.json # RETURNS MY JSON LIST
format.partial_html # RETURNS MY PARTIAL HTML
end
end
end
Is there anyway to accomplish this in RoR? Or am I doomed into having to create another action in my controller just to return technically the same data?
Could I make this happen by specifying my own MIME type? Should I snake in the partial as an XML return type?
Use format.js on the third line.
Put the partial html on a partial, call it app/views/users/_html_rows.html.erb.
render that partial both on the full html and on the js version.
You will have app/views/users/list.html.erb with the full html content, which will be something like this:
<html>
<body>
.....
<table id="my_table"><%= render 'users/html_rows', users: #users %></table>
</body>
</html>
You will have app/views/users/_html_rows.html.erb with:
<tbody>
<% users.each do |user| %>
<tr>
<td>user.name</td>
</tr>
</tbody>
Then you will have app/views/users/list.js.erb with:
$("#my_table tbody").html("<%= render 'users/html_rows', users: #users %>");
This probably will solve your problem.
You can add an additional mime type entry to work with respond_to. In config/initializers/mime_types.rb, add:
# htmlp means "html partial"
Mime::Type.register "text/html", :htmlp
In your controller you can now do:
def list
respond_to do |format|
format.html
format.json
format.htmlp { render layout: nil }
end
end
And create a template called list.htmlp.erb with your partial content in it.

How can I load a embedded ruby javascript file on pageload?

So, as I am sure you are all familiar with, you can have actions in Rails that call html.erb files. You can also set up actions to render remotely that call embedded ruby files (for example submitting a "post" form to the "posts" controller, handling it remotely, and calling a js.erb file to update elements in the page).
What I want to know is how to run a js.erb file when I'm running an action that loads a template (html.erb file). To explain, consider if I want to run a User Show page:
class UsersController < ApplicationController
def show
#user = User.find(params[:id])
end
end
Linking to user_path(User.find(1)) will load show.html.erb, and all seems great.
But what if I want to click on a link to user_path(User.find(1)) and load show.html.erb while also loading show.js.erb? If (and I hope it is) this is possible, how could I adapt the show definition to also load show.html.erb and custom_js_file_name.js.erb?
FYI: I'm using Rails 3.0.9 and 3.1.3 on two different applications, and assume that I would put show.js.erb or any others in the Users folder (views/users/...)
By default,
def show
#user = User.find(params[:id])
end
will only render the show view based on if it was requested via HTML, JSON, JS, etc.
I think what you are describing is better suited for the render method in Rails. Typically, controllers use the different format.(:format) methods in the respond blocks to respond to a controller call based on what the request type was (JSON, HTML, JS, etc).
In your show.html.erb file:
<%= render "users/show.js" %>
This allows you to render any arbitrary file you want in another one of your views. This also allows you to split up your large view files into smaller (reusable) pieces called partials (note: all partials are named with a _ character at the beginning and are called via <%= render :partial => "users/my_partial" %> which would render the _my_partial.some_format.erb file)
Try having the show action render the show.js.erb file when requested with a format of js. That should get Rails to render the dynamic template; now link to it from the original show.html.erb with a javascript link tag.
In show.html.erb:
<script type="text/javascript" src="<%= show_users_path(#user.id) -%>.js"></script>
I haven't tried this and the rendering of show.js.erb may put additional formatting that would be a problem.

Don't render layout when calling from Ajax

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

Resources