How to show one model in two different layouts? - ruby-on-rails-3

I have two layouts: admin_layout.html.erb and application.html.erb. The admin works in the admin_layout and creates Users. There is a show page for each User under the admin_layout. But now I would like the general public to view a public version of each User's show page under the application layout. Is it possible to display two different show actions depending on the layout or do I need to build something like a public_users_controller?

You can specify which layout to use in your action, and you can also tell it to render a different template than the current action's name. In this example, public users would be shown the app/views/users/public_show.html.erb file using the app/views/layouts/application.html.erb layout.
def show
if current_user.admin? # Or however you want to determine which layout to use
render :layout => "admin_layout"
else
render "public_show", :layout => "application"
end
end

Related

How to give option for multiple templates for one view?

I have an application in rails3 where i want to give user an option to choose a template from defaults (4 or 5 templates) to view his records.
the approach i am working on is to sent user on setting page from where he will select the template he want to use and on the basis of that setting the template will be rendered.
This looks simple but i am not sure this will work for me, please suggest me any alternative.
Please note i am talking about PDF formats.
Let's say you have a Setting model with a string attribute template.
You would let the user save the setting through a normal controller action.
Then on the controller where you want your pdf template to show, you could do something like this.
class MyController < ApplicationController
def show
#setting = Setting.find(params[:setting_id]) # Retrieve the setting instance
respond_to do |format|
format.pdf { render setting.template }
end
end
end
This will render the template named after the template the user has selected and is stored on setting as a string.

rails render form after error in other controller

I have two models in the following releationship: Client has_many products, and Product belong_to client. In the client show view I present a form to create new products, that automatically belong to the current client. The show method in the client controller
def show
#client = Client.find(params[:id])
#products = #client.products.paginate(page: params[:page])
#product = #client.products.new
#product.client_id = #client.id
end
and the show view renders a partial
<h1>New Product:</h1>
<%= render 'shared/product_form' %>
That works, products are correctly created.
When an validation error occurs I set a flash in the product create method and redirect_to the client show page. There I loose the data that has been filled in correctly. I tried save the #product instance variable, which has all the data (including the wrong fields) doing
render client_path(client)
from the product controller, but that produces an error
Missing template /clients/17
with the address being
http://localhost:3000/products
Am I calling this wrong? I know that render ususally renders action of the same controller. Can I somehow render Client::show from the product controller? Is there another way to save the data the user has typed in?
If a validation error occurs you should redirect back to the page that generated the validation error. Ie: if the user is at products/new when they submit the form, then your products#create action should end with render :new to present the products form again.
If your products#create action is receiving the form from clients#show, then you do want to render clients#show with validation errors. In that case, all the information that was completed in the form will be available at params[:product], just like it is coming in to products#create.
You might want to take a look at another answer I wrote recently to understand the flow between controllers.
Specifically, the misunderstanding in your case is as follows:
When you have a validation error the record will not save, so you cannot "go back to that data" because your app has not kept it anywhere. The only copy of the data that was submitted is in the request.
If you REDIRECT you are not forwarding a request, you are responding to the initial POST request (which includes all the form information as params[:product]) by making a new request to a different url. This is why you want to use RENDER.
However, if you try to render clients_path(client), what happens is Ruby will first evaluate clients_path(client) to the string clients/(client.id), or in the example you gave, clients/17.
Then render tries to call render 'clients/17', which you don't have a template for. It's looking for a file called clients/17.html.erb. That's why you get the error you're getting.
So, again, to sum up - your products#create action receives the information that was sent from the form as params[:products]. That information is not available outside of this controller action. So, if there is a validation error, instead of creating the product, this controller action should render the same page that the user came from originally (normally products/new) so that they can see the form they just had (with the information filled back into it if you're using a form builder) and also see the error that prevented saving.
I hope that makes sense, feel free to ask follow-up questions.
Yes, you were calling it wrong.
There are three problems:
render should render a template name, say client/new, shared/form etc. The argument could not be a path or variable. The variable is passed by controller to view, nothing to do with render.
You should not use render for a saving fail. Even if you use a template name as #1 mentioned, you'll end up with a wrong URL like products/create with the client page. That's not acceptable.
My suggestion is to always use redirect for saving fail.
Minor problem. In controller, if you've used #product = #client.products.new, the #product object will have every attributes empty but with a valid client id. So you don't need to assign client id again by #product.client_id = #client.id. But this doesn't hurt the result.
Andrew's great answer plus this:
Where to render comments controller in Rails on model validations failure?
makes the solution more clear.
Your specific example: Just watch out that your create action in the ProductsController has all instance variables it needs to render 'clients/show' from it.

Destroy-like functionality using a custom controller method in Rails 3

I have a resource called patient_admissions that has all the RESTful routes. It is nested under another resource called patients. I want to add another method to my patient_admissions controller called discharge that updates a field in the model called :discharge_date (with Date.now) and saves that value in the table.
I would like this to work like the destroy method, in that if I have a bunch of patient_admission objects listed in a table in my index view, I could just click on the Discharge link and a confirmation box would appear, I would click 'ok' and then the value would be updated without having to first go to another view and deal with forms.
How can I do this without resorting to something like javascript? Many thanks!
In the rails guide on Routing, there's a section on adding additional restful actions:
http://guides.rubyonrails.org/routing.html#adding-more-restful-actions
The example there would translate to something like:
resources :patient_admissions do
member do
put 'discharge'
end
end
This will recognize /patient_admissions/1/discharge with PUT, and route to the discharge action of PatientAdmissionsController.
This will at least allow you to get the routing set up for the action.
You could do this by using the link_to or button_to helpers in conjunction with a custom member route for your controller. Here is an example:
#routes.rb
resources :patient_adminssions do
put :discharge, :on => :member
end
Notice that I used PUT to add the custom route because the record will not be deleted, just modified. So according to the REST standards, I think put is the most appropriate.
# in your view
<%= button_to "Discharge", discharge_patient_admission_path(#patient_admission), :method => :put,
:confirm => "Are you sure you want to discharge this patient?" %>
This will create a button in a hidden form that when clicked will display the confirmation message and if it is confirmed then it will send a request to your controller action where you can set the appropriate discharge date like you suggested.

Rails 3 render layout except for root_url

I want to have a different layout for the home page (root_url) than the other pages in my web app. I would like to be able to use <%= render 'layouts/pages' %> for pages that are not the home page. How do I go about doing this?
Majority
The best way to do this is to name the layout for the majority of your app layouts/application - this way, Rails will automatically assume this layout for that majority without you needing to do anything else.
Home
For your home page, you can add this line to the bottom of your controller action:
render :layout => "home"
This will tell Rails not to use application, but instead to point to your home/root page's layout, which in this case would be located at layouts/home.

Rails 3, menu in layout

i have some controllers, i want to put categories names into layout, like menu, what i could see in all pages. Is there a easy sollution to do this?
Use : render :layout => 'yourcustomlayout'
in your specific action.
Use general layout for whole application. You can specify one layout for an application and even override it wherever required.
See here for details and near exact example for your case.