we have lots of panel in out Application like admin , teacher principal , student , parent etc .
Each panel have its own layout
So upon login we handle this using WelcomeController
class WelcomeController < ApplicationController
def index
respond_to do |format|
format.html do
return render :home if current_user.nil?
return render :admin if current_user.super?
return redirect_to("/student/lesson") if current_user.student?
return redirect_to("/teacher/lesson") if current_user.teacher?
return render "layouts/principal" if current_user.principal?
return render "layouts/coordinator" if current_user.coordinator?
return render "layouts/viceprincipal" if current_user.viceprincipal?
return render "layouts/parent" if current_user.parent?
end
end
end
end
So right now for getting data from controller we redirect to his route Like for Student
return redirect_to("/student/lesson") if current_user.student?
but we wants that on URL / we get data from controller .
So my problem is how to get data ? So we can use in views
I am new to Rails , if I am using something wrong Please let me know . Will I get data from Model ?
In routes we use
get '/student/lesson', to: 'student_lesson_plan#index', as: 'student_lesson'
And from index Action we have variables which we use . So I want
instead of
return redirect_to("/student/lesson") if current_user.student?
something like this
return render "layouts/student" if current_user.student?
And I can use those variables which I initialize in student_lesson_plan#index or from another place
In my application I would set something like this to get different layout depending on conditions:
class ApplicationController < ActionController::Base
# Prevent CSRF attacks by raising an exception.
# For APIs, you may want to use :null_session instead.
protect_from_forgery with: :exception
layout :layout_by_user_types # works like a before action
def layout_by_user_types
if current_user.student?
"students/application"
elsif current_user.other_condition?
"other_name_space/application"
else
"application"
end
end
end
In my views folder I would separate the different layouts so I can call different css/js if needed ...
-views
-layouts
-students
-_my_partials.html.erb
-application.html.erb
-other_users
-_my_partials.html.erb
-application.html.erb
....
The basic understanding of how we get info from models to views thanks to the controllers:
In a controller
class StudentController < ApplicationController
def_index
#my_var_i_want_in_my_view = Student.my_query_to_database
#my_var_i_want_in_my_view_too = Student.my_super_action_that_will_give_some_data_and_that_is_a_method_in_my_model
end
end
Then in the view you can grab and use #my_var_i_want_in_my_view
Related
I have a single-page application written in React with Ruby on Rails back-end (API mode). Rails is also serving static files. I'm pointing Rails router to public/index.html, so my SPA could manage his own routing with react-router. This is common practice in order to make direct links and refresh to work.
routes.rb
match '*all', to: 'application#index', via: [:get]
application_controller.rb
class ApplicationController < ActionController::API
def index
render file: 'public/index.html'
end
end
The problem is this doesn't work in API mode. It's just an empty response. If I change the parent class to ActionController::Base everything works as expected. But I don't want to inherit the bloat of full class, I need slim API version.
I've tried adding modules like ActionController::Renderers::All and AbstractController::Rendering without success.
If I change the parent class to ActionController::Base everything works as expected. But I don't want to inherit the bloat of full class, I need slim API version.
Yes, if you serve index from ApplicationController, changing its base class would affect all other controllers. This is not good. But what if you had a specialized controller to serve this page?
class StaticPagesController < ActionController::Base
def index
render file: 'public/index.html'
end
end
This way, you have only one "bloated" controller and the others remain slim and fast.
You could do
render text: File.read(Rails.root.join('public', 'index.html')), layout: false
I usually just redirect_to 'file path'.
def export
# When the route coming to get 'some_report/export', to: 'greate_controller#export'
# The file where you write or preparing, then you can redirect some path like : http://localhost:3000/public/tmpfile/report20210507.xlsx
# And it will just redirect the file for you
file_path = "public/tmpfile/report20210507.xlsx"
redirect_to "#{root_url}#{file_path}"
end
For this example
root_url = "http://localhost:3000/"
This should work, and allow you to keep inheriting from ActionController::API--
class ApplicationController < ActionController::API
def index
respond_to do |format|
format.html { render body: Rails.root.join('public/index.html').read }
end
end
end
The render logic changed for ActionController::API with Rails 5.
I am trying to apply a specific layout to one action in a controller in my Rails (v. 4.2.5) app, but it will not work. Oddly (or maybe not so oddly), the layout will be used used to render an action if that action is part of the 'resources' route, but not to the action that I need this to work for, which is not part of 'resources'. Sorry if that seems confusing, here's the relevant code and explanation...
routes.rb - here I have the standard resources routes for my entries controller, as well as a an additional route for 'inputs'
get '/entries/inputs' => 'entries#inputs.html'
resources :entries
entries_controller.rb - here I'm trying to apply the layouts/cached.html.erb to the 'inputs' action
class EntriesController < ApplicationController
layout "cached", only: [:inputs]
def inputs
end
def index
#entry = Entry.all
end
end
As it is, layouts/cached.html.erb does not get applied to the 'inputs' action. However, if I swap out the second line of code in the controller for this:
layout "cached", only: [:index]
the layout is successfully rendered for the 'index' action.
What am I missing here? Why will this layout apply to one action but not the other?
Use this code:
class EntriesController < ApplicationController
layout :resolve_layout
def inputs
end
def index
#entry = Entry.all
end
private
def resolve_layout
case action_name
when "inputs"
"cached"
else
"application"
end
end
end
And in Routes:
get 'entries/inputs' => 'entries#inputs'
What I need is to disable the automatic page (HTML) rendering in rails and override it with a after_action method. What I'm trying to achieve is an equivalent of CakePHP $this->autoRender = false;
application_controller.rb
class ApplicationController < ActionController::Base
after_action :custom_render
layout nil # Tried this but didn't worked
def custom_render
render #[...]
end
end
some_controller.rb
class SomeController < ApplicationController
def index
# No rendering here
end
end
As shown in the code I tried to add a layout nil to prevent all actions from rendering, but that doesn't seem to affect the behaviour of the action.
Haven't checked whether it works with Rails 4, but this patch works for Rails 5.
According to the code of BasicImplicitRender and ImplicitRender, send_action of is BasicImplicitRender responsible for calling default_render
Documentation says:
For API controllers, the implicit response is always 204 No Content.
For all other controllers, we use ... heuristics to decide whether to
render a template, raise an error for a missing template, or respond with
204 No Content ...
So I suppose redefining default_render method will serve you purpose.
In your controller:
def a
# uses `default_render` unless you call `render` method explicitly
end
def b
render plain: 'Custom text for b' # `default_render` won't be called
end
private
# This does the trick
#
def default_render
render plain: 'Text'
end
You may also hack send_action just like it is done in Rails so as to even skip default_render call at all:
module ActionController
module BasicImplicitRender # :nodoc:
def send_action(method, *args)
# super.tap { default_render unless performed? }
super
end
end
end
To disable rendering (well return nothing) issue.
def index
render :nothing
end
But it's too late to do anything, as it will return response with empty body.
To disable layout:
def index
render layout: false
end
This will render you view without a layout, issue (render layout: 'my_custom_layout') to render default view but with different layout.
We don't know what you want, but the simplest solution is just to render a specific view, f.i.:
def index
render 'my_custom_file.'
end
There are really many options: http://guides.rubyonrails.org/layouts_and_rendering.html#using-render
EDIT - as requested in a comment
class ApplicationController < ActionController::Base
before_action :set_user_template
# ...
def set_user_template
template_name = current_user.template_name
self.class.layout "#{template_name}/application"
end
end
I'm serving a versioned web service from Rails.
I would very much like to be able to call render like normal:
render 'index'
And have it correctly serve the requested version from the following:
index.v1.json.jbuilder
index.v2.json.jbuilder
index.v3.json.jbuilder
Assuming that I already know the requested version within the context of the controller action execution, how do I get render() to leverage it?
I have used the versioncake gem
You should definitely check this out. File name will be very close to what you have:
index.v1.json.jbuilder
would be
index.json.v1.jbuilder
Sounds like a builder design pattern might work here. Have a view builder object that returns the desired behavior.
module ViewBuiler
def build(name, api_version)
View.new(name, api_version).build
end
class View < Struct(:name, :api_version)
def build
[name, api_version, 'json', 'jbuilder'].join('.')
end
end
end
and in your controller you could just do something like:
ApplicationController
include ViewBuilder
end
MyController < ApplicationController
def index
...
# you can pass either strings or symbols to build and it will
# return 'index.v1.json.jbuilder'
render build(:index, params[:api_version])
end
end
And disclaimer, this is just an idea and not something I've implemented. I like the approach for 2 reason. The Controller actions remain skinny and don't have logic in it. A builder like this seems pretty easy to test. And it keeps the thing that might change (views etc) isolated into something that can change frequently as long as it retains it's interface that the Controllers will work with.
This seems like a candidate for Variants. This is new to 4.1 though.
class MyController < ActionController::Base
before_action :set_variant
def my_action
.....
respond_to do |format|
format.json do |json|
json.v1 do
# render whatever you need here
end
end
end
end
protected
def set_variant
request.variant = :v1 if request.params[:version] == "v1"
....
end
end
I need message to have a different layout in project, is it possible in rails to do something like this?
Class Messages::New < #project? ProjectLayout : NormalLayout
end #i treid this, don't work, since #project has not been initiated.
thanks
this may help you
class MessagesController < ApplicationController
layout :get_layout
def get_layout
#project? ? 'ProjectLayout' : 'NormalLayout'
end
end
Also, since the question is unclear, you can also set layout for only one action with the render option.
render :action => 'new', :layout => 'layoutname'
You can only apply layouts at the Controller level:
class MessagesController < ApplicationController
layout :project
end
Layout method documentation has an example on how to do conditional layouts
You can only apply the rails layouts at controller level and individual action levels.
Unique layout in for each controller
class MessagesController < ApplicationController
layout "admin"
def index
# logic
end
end
** The above line layout "admin" will load the admin layout each time the message controller gets invoked. For that, you must have a layout created in your layouts/admin.html.rb file.**
Dynamic layout for each controller
class MessagesController < ApplicationController
layout :dynamic_layout
def index
# logic
end
protected
def dynamic_layout
if current_user.admin?
"admin" # Show admin layout
else
"other_layout" # Show other_layout
end
end
end
# Individual Action Level Layouts
If you want to display different layouts for each action you can do that.
class MessagesController < ApplicationController
layout :dynamic_layout
def index
# logic
render :action => 'index', :layout => 'index_layout'
end
def show
# logic
render :action => 'show', :layout => 'show_layout'
end
end
Decide the layout in the controller rather then the model. Your ProjectsController can use it's own ProjectLayout and MessagesController can then use the normal layout if you wish.
My two cents in ApplicationController:
before_action :layout_by_action
##actions = %w(new edit create update index)
def layout_by_action
if ##actions.include? params[:action]
self.class.layout 'admin'
else
self.class.layout 'application'
end
end
You can use a Proc:
layout -> {
if something?
'my-layout'
else
'my-other-layout'
end
}