How to ask two queries from a single controller - ruby-on-rails

I am trying to do everything on a single page. The page is called users/index.html.erb.
On this page I return a simple data base query in json format.
I would like to also return a seperate query in non json format from the same database.
I'm trying to avoid partials for the moment as they make variable handling a bit more complicated that I want for my beginner level.
This is what I have:
class UsersController < ApplicationController
def index
#users = User.including_relationships
#followas= User.find_by name: params[:name]
respond_to do |format|
format.html # index.html.erb
format.json { render json: #users }
end
end
end
but it doesnt work unsurprisingly. How can I get it to work in one action?

Not sure about your purpose of returning two formats of query from a single controller call. It usually happens when you use Javascript heavily (eg. Angular.js). Controller first returns the HTML page including all the HTML template and javascript, then data in JSON is returned in the second round.
So in your users/index.html.erb, you might have something like:
<p ng-repeat='user in users'>{{user.name}}</p>
<script>
// do a AJAX call to retrieve users in JSON format
</script>
Then you will call users/index.json again to retrieve users information in JSON.
In short, your users/index is called twice, once for html, once for JSON.
It might seem unwise to call the same controller method twice just to render different format. In such case, you might consider to embed the JSON data in HTML. In such way, you will only render users/index.html.erb once and parse the data in JSON through Javascript on the client side.

Related

How does respond_to and respond_with work in rails?

When there is
def some_action
respond_to do |format|
format.html {}
format.js {}
format.json { respond_with #objects}
end
end
It seems like html line and the js line automatically serve up/call the file matching the action's name. And the html and the js are serve up one or the other, not both. Is this correct?
The json gets called if you have an ajax call in your js that got called, and it requests data, and these need data to respond with, right? Do I need it to respond to json and to js, or just one?
If you don't to respond_to, and omit all the types, does it by default respond to html and to js?
When I do respond_to in the controller, rather than a respond_to block within each action, does using respond_with #objects apply to any argument (:thml, :js, :xml, :json, etc)?
Alternate syntax:
class TheController < ApplicationController
respond_to :html, :js, :json, only: [:some_action, :other_action]
def some_action
respond_with #objects
end
end
How does the alternate syntax work?
If you use the alternate syntax, can you not respond differently to different types of requests? Do you have to do a respond_to block isntead of the alternate syntax if you want to respond differently? How do each of these cases address graceful degradation to html?
respond_with
For a given controller action, respond_with generates an appropriate response based on the mime-type requested by the client.
This basically means your controller will send the appropriate data on a request basis - for example, if you did the following:
#app/controllers/articles_controller.rb
Class ArticlesController < ApplicationController
def show
#article = Article.find params[:id]
respond_with #article
end
end
This would basically respond with the data from #article each time you send a request. If the request is in the json mime-type, it will come back as a JSON object; if it's an HTML request, it will come back with the HTML object on the show view
--
respond_to
Basically allows you to tailor the specific responses to different mime-types. If you send a JS request, you can manage the JS response etc
respond_to blocks inside the controller actions are very cumbersome, and only really meant for specific changes / edits to the response itself.
A much simpler way to handle the respond_to is to declare it at the top of the controller file, essentially telling Rails that every action will use the options defined in that method:
#app/controllers/your_controller.rb
Class YourController < ApplicationController
respond_to :js, :json, :html #-> the same as using respond_to block for each action
end
note that in Rails 4 respond_with feature has been extracted to the gem 'responders' (https://github.com/plataformatec/responders).
The cases of when you need/don't need each format.*whatever* line.
Normally, you don't need any of it. Rails by default looks for an html file (aka template) matching the action name under the view folder matching the controller's name.
I'm not sure when/why the json and html are sometimes paired together(like in the scaffold-generated code). Perhaps the json line is for turbolinks (please confirm/correct this). But I do know you use a respond_to block with various types of format lines when you want each type to behave differently (e.g. serve up 10 results at a time through js, but more results through html).
The js format is needed when you use remote: true in a form or link. This is because Using this remote: true disables the html template from being served up, and instead looks for a js file matching the action's name, and executes/renders that file. You don't actually need the respond to json line if you're doing things in js only.
Bonus hint: if your js files have js.erb, you can access the instance variables (how about local variables? please confirm/correct this) that you set in your action. This kind of makes sense because your *.js.erb file is technically a view. Views can access its corresponding actions' variables (hmm what about when vies get rendered from another controller?). So if you already have access to your action's variables in your js file, this can eliminate the need to make ajax calls or json calls in many situations.
I'm actually not sure when you need the json line when also using remote: true / javascript. explicit jQuery.ajax() method calls that want json data might warrant the use of the respond to json line.

How to create a Rails model without an HTML view

I have a model that I only want to ever return JSON, regardless of any conneg or file-like extensions on the URI (e.g. /app/model.json). Google-fu is coming up short and this can't be that difficult.
In your controllers you simple have to create a respond_to block that only responds to JSON:
respond_to do |format|
format.json { render :json => #model }
end
This is actually a decision made by the controller, not because there is/is not a model or view present. In your controller you can:
render json: #your_model
However you will quickly find that the default implementation of to_json (which is what is used internally, above) can be annoying hard to do exactly what you want. When you reach that point you can use RABL to create views that massage the JSON from your model(s) exactly as you want.

Rails/Ajax/Error Processing

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.

Passing json from index action and rendering of another application layout simultaneously. Ruby on Rails

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.

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.

Resources