Render partial template base on condition in Ruby on Rails - ruby-on-rails

I have 2-column layout. Some controller has left column, some not. What is the best way to render it depend on controller? Now, it look likes that:
<% if params[:controller] != 'page' %>
<div id="navigation" class="l"><%= render "layouts/left-menu" %></div>
<% end %>
It's bad, bad monkey code.

Edit: Changed my solution, OP wanted condition to depend on action and controller.
In your base helper, define a method like this:
# app/helpers/application_helper.rb
module ApplicationHelper
def has_left_menu?
#has_left_menu.nil? ?
true : # <= default, change to preference
#has_left_menu
end
end
In your application controller:
# app/controllers/application_controller.rb
class ApplicationController
def enable_left_menu!
#has_left_menu = true
end
def disable_left_menu!
#has_left_menu = false
end
end
In your view or layout, change your check to this:
<% if has_left_menu? %>
<div id="navigation" class="l"><%= render "layouts/left-menu" %></div>
<% end %>
Now you can disable/enable the left menu in before_filters or anywhere else in your action:
class UsersController < ApplicationController
# enable left menu for "index" action in this controller
before_filter :enable_left_menu!, :only => [:index]
# disable left menu for all actions in this controller
before_filter :disable_left_menu!
def index
# dynamic left menu status based on some logic
disable_left_menu! if params[:left_menu] == 'false'
end
end

In your controller you use layout like this
#PublicController is just an example
class PublicController < ApplicationController
layout "left-menu"
end
And in the views/layouts folder you put the left-menu.html.erb
with a stylesheet_link_tag to your spesific css file
<%= stylesheet_link_tag 'left-menu' %>
You can learn more at rails guides

Related

In Rails how to cleanly only execute code if variable is set to true?

In my layouts/books I have my navigation code that is hitting all my views for books then yields.
I also have my header code that is hitting all my views for books then yields. Really I only want my header code to hit the index and show but not my order.
I've tried putting something like below around my header to:
<% if #render.header == true %>
<div class="header">
HTML
</div>
<% end %>
Then putting in my views:
<% if #render.header == true/false %>
I end up getting undefined method `header'. I've also tried things like:
What do I need to adapt this into to get this to work? Should I be putting something into my controller?
EDIT: I've also attempted the following in both my layout/books and in index:
<% #render.header = true %>
This leads to undefined meathod "header= "
I can get this to work using a global variable but that has got to be a bad idea.
You can do this:
if #render && #render.header
So after flailing around I went from in my layout/book:
<% if #render.header == true/false %>
To:
<% $header == true/false %>
Which worked but it's global. And finally to updating my controller for both index and show to:
def index
#header = true
end
And then my layout/book to:
<% #header == true %>
So yeah. Variable types.
This is nothing terribly different from what I already pointed out in the comments but here it goes:
First of all, #render just defaults to nil in views. So when you are trying to set the value for header, it fails with undefined method 'header=' for nil:NilClass (because Rails looks for a method called #header= for the nil:NilClass).
Use a plain variable #render_header
You can instead just set a plain variable, such as #render_header, in your controller or view (before rendering header):
<% #render_header = true %>
and then in your header
<% if #render_header %>
# ...
Use a hash for #render = { header: true }
Or, if you really want to use the #render variable, you can just set it to be a hash:
<% #render = { header: true } %>
and then in your header
<% if #render.present? && #render[:header] %>
# ...
This is almost like what you originally wrote.
Set the variable automatically based on controller action
If you need this variable to be set for multiple controller actions, you can look into controller filters. They work just like callbacks but for controllers.
For example, you could do:
class MyController < ApplicationController
before_action :set_render_header_to_true, only: [:new, :show]
# ...
private
def set_render_header_to_true
#render_header = true
end
end
That way, the variable would automatically be set to true for the :new and the :show action. If you had multiple controllers needing to set render_header to true, you could move the set_render_header_to_true method to the ApplicationController. For example, this would work:
class ApplicationController
private
def set_render_header_to_true
#render_header = true
end
end
class MyController < ApplicationController
before_action :set_render_header_to_true, only: [:new, :show]
# ...
end
class MyOtherController < ApplicationController
before_action :set_render_header_to_true, except: :index
# ...
end
Set a custom layout from your controller
You can also specify a specific layout to be rendered based on the controller action:
class MyController < ApplicationController
layout 'with_header'
# Use the line below to render header only for specific actions,
# such as new and edit
# layout 'with_header', :only => [:show, :edit]
# ...
end
You can even define a method to dynamically choose the layout:
class MyController < ApplicationController
layout :determine_layout
def determine_layout
if action_name == :new
'layout1'
elsif action_name == :show
'layout2'
else
'application'
end
end
# ...
end
I don't think i understand your problem. Anyway you can also use content_for. For example, if add to layout/books.html.erb:
<%= yield :header %>
For index/show/etc views with header:
<% content_for :header do %>
<div class="header">
HTML
</div>
<% end %>

Using a controller variable in application html.erb

I'm probably trying too hard for my first website but i wanted to make a dropdown on a (bootstrap) navbar to be flexible, and show the names of the saved work categories.
This is what i've tried to do in the application.html.erb file :
<ul class="dropdown-menu">
<% #workcategory.each do |workcategory| %>
<li><%= workcategory.name%></li>
<% end %>
Failed with error undefined methodeach' for nil:NilClasson the<% #workcategory.each do |workcategory| %>` line.
This is the workcategories controller :
class WorkcategoriesController < ApplicationController
before_action :find_workcategory, only: [:edit, :update, :destroy]
def index
#workcategories = Workcategory.all.order("created_at DESC")
end
def new
#workcategory = Workcategory.new
end
def create
#workcategory = Workcategory.new(post_params)
if #workcategory.save
flash[:notice] = "Workcategory created"
redirect_to(:action=>'index', :workcategory_id => #workcategory.id)
else
#workcategories = Workcategories.order()
render('new')
end
end
def edit
end
def update
end
def destroy
#workcategory.destroy
redirect_to workcategory_path
end
private
def find_workcategory
#workcategory=Workcategory.find(params[:id])
end
def post_params
params.require(:workcategory).permit(:name)
end
end
Any tips and help are welcome, even non-related to the initial question :) Thank you
If you want it in all ur actions, it is wise to put it in your application_controller.rb.
before_filter :set_work_categories
def set_work_categoriers
#w_categories = Workcategory.all.order("created_at DESC")
end
This should work fine.
Also, a tip.
You can use default_scope {order(created_at: :desc)} in your model WorkCategory.rb
Then you can use this like,
def set_work_categoriers
#w_categories = Workcategory.all
end
I would recommend changing the variable name to #w_categories or else it will conflict with your #work_categories name in index action.
In your application.html.erb file, change
<% unless #w_categories.nil? %>
<ul class="dropdown-menu">
<% #w_categories.each do |workcategory| %>
<li><%= workcategory.name%></li>
<% end %>
</ul>
<%end>
I guess this should do the trick
If we talk about index action, then you just forgot to use appropriate variable:
<ul class="dropdown-menu">
<% #workcategories.each do |workcategory| %>
<li><%= workcategory.name%></li>
<% end %>
Update
If you want to have this in all actions, then initialize #workcategories in before_action:
# your_controller.rb
before_action :initialize_work_categories
def initialize_work_categories
#workcategories = Workcategory.all.order("created_at DESC")
end
Layouts
application.html.erb is a layout, meaning that it will be present regardless of whether you're using the Workcategories controller or not.
If you want to load a variable into the layout, irrespective of which controller is being invoked, you'll need to make sure the #workcategory variable is present.
To do this, you would generally put the #workcategory declaration into the ApplicationController (which most other controllers inherit from):
#app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
before_action :set_categories
private
def set_categories
#workcategory = ....
end
end
This is the standard way to populate layout-side variables. It's not efficient and only works if your controllers inherit from the Application controller.
Some further pointers would include the following:
1. Object orientation
Ruby, and by virtue, Rails, is object orientated.
This means that everything you do should revolve around objects. This is why Rails has many helpers that seem to work "magically".
They're not magic at all - they just take the objects you pass them, and build pre-baked HTML to give you specific functionality.
A good example are the routes:
#config/routes.rb
resources :controller
The reason this is important is that when you call actions / variables, you need to think of them as objects. This is difficult to do for newbies, but if you can get your head around it, it helps your coding massively.
--
2. Controllers
Following on from the above, you have to remember that your controller is really just a way to manipulate objects.
Thus, if you're calling #workcategory, you need to appreciate where the object is going to come from and how it's going to be populated.
You can make a helper method work_categories and use that. Either define it directly in application_helper - or if you don't want to use helpers you can put this code in your ApplicationController
class ApplicationController < ActionController::Base
def work_categories
Workcategory.all.order("created_at DESC")
end
helper_method :work_categories
end
Then in your view:
<ul class="dropdown-menu">
<% work_categories.each do |workcategory| %>
<li><%= workcategory.name%></li>
<% end %>
</ul>

How to link model to controller action

I have a Slider model in my project and it has a lot of polymorphic associations with other model like Product, Manufacturer, Article and etc.
So, when I use 'show' action with one of the models I also show related Slider. It's ok. But sometimes I need to show Slider with 'index' action.
What is the best way to link some of the sliders to actions, not to other models?
UPDATE
routes
resources :products, :articles, :industries, :manufacturers, only: [:index, :show]
Product controller
class ProductsController < ApplicationController
load_resource
# GET /products
# GET /products.json
def index
#catalog = Product.by_type_and_manufacturer
end
# GET /products/1
# GET /products/1.json
def show
#page_slider = #product.slider
end
end
So in 'show' action I just use product.slider to get related Slider instance. But I want to show another slider for all products by index action.
In that case, what you're trying to do is not possible. You cannot create a relation to a controller action. What you need to do is link the relation's controller action, rather than trying to create a relation to the controller action. A model can only be related to another model (you cannot has_many index, show, delete, etc...)- In other words, call up the data for the relation, and link to that relation's controller action in the view.
example:
#Models:
class Page < ActiveRecord::Base
has_many :sliders
end
class Slider < ActiveRecord::Base
belongs_to :page
end
#Controllers
class PagesController < ApplicationController
def index
#pages = Page.all # lists all pages
end
def show
#page = Page.find(params[:id]) # simplified, this will probably make use of strong params in your actual code
#sliders = #page.sliders # all sliders related to the page
end
# if you would like to show a page that just has all sliders for a specific page and not the page itself...
def show_page_sliders # you will have to create a route and view for this manually
#page = Page.find(params[:id]) # simplified, this will probably make use of strong params in your actual code
#sliders = #page.sliders # all sliders related to the page
# note that this controller action is identical to the show action, because the data we're going to need is the same- the difference comes in the view
end
end
class SlidersController < ApplicationController
def index
#sliders = Slider.all
end
def show
#slider = Slider.find(params[:id])
end
end
# Views
# page#index
<% #pages.each do |p| %>
...
page listing code goes here. if you want to list the sliders for each page on the index...
<% p.sliders.each do |s| %>
...
individual slider info goes here
...
<% end %>
...
<% end %>
# pages#show
<%= #page.name %>
<%= #page.content %> <!-- or whatever data you have for page -->
# since here we are showing a singular page, we can just use our #page instance variable to list out the sliders
<% #page.sliders do |s| %>
...
Slider listing code goes here
...
<% end %>
# pages#show_sliders
<!-- this is identical to the page#show view, minus the actual page info, and with the addition of a link back to the parent page -->
<%= link_to "Back to page", page(s.page_id) %>
<% #page.sliders do |s| %>
...
Slider listing code goes here
<!-- you can link to any path from the slider listing -->
<%= link_to "Show", slider(s.id) %>
<%= link_to "Edit", edit_slider_path(s.id) %>
<%= link_to "Delete", delete_slider_path(s.id) %>
...
<% end %>
#######################UPDATE#############################
# to define one slider per controller action
class PagesController < ApplicationController
def index
#pages = Page.all
# you need to add a "controller_action" column to your Slider model
#slider = Slider.find_where(controller_action: "pages#index")
end
def show
#page = Page.find(params[:id])
#slider = Slider.find_where(controller_action: "pages#show")
end
# etc ...

Why my helpers does not outputting nothing in my view?

I'm new on rails and I have a book to study them. In one practice, I created a helper in my Application Helper, the test from RSpec work fine, until I have to print the result of my helper. No show any result and no error happens.
application_helper.rb
module ApplicationHelper
def title(*parts)
unless parts.empty?
content_for :title do
(parts << "Ticketee").join(" - ")
end
end
end
end
show.html.erb
<% title(#project.name) %>
projects_controller.rb
class ProjectsController < ApplicationController
def show
#project = Project.find(params[:id])
end
end
and when I go to the show link I supposed to see "Random Project name - Ticketee", however only they show me "Ticketee".
Any help...
<% title(#project.name) %>
Means don't show to the user
<%= title(#project.name) %>
Means show to the user - notice the equals.

Where does the site-wide footer logic belong in a Rails 3 app?

I have a site-wide footer that should display a list of recent Users and Posts. I'm wondering where the logic should to gets this data. Should I have a "recent_users" method in the UsersController and a "recent_posts" method in the PostsController, or should I have a separate FooterController?
How about a _recent_users partial views/users and a _recent_posts partial in views/posts and have the footer partial render both of them?
All "business logic" should be put in the Model, not the controller. The query for recent Users and Posts should be in the User and Post model. Then, if you have a site-wide view element, move it into a partial and add that partial into the application.html.erb.
# User.rb
model User
def recent
# logic and query here
end
end
# Post.rb
(see above)
# application_controller.rb
before_filter :get_recent_posts
before_filter :get_recent_users
...
private
def get_recent_posts
#recent_posts = Post.recent
end
def get_recent_users
#recent_users = User.recent
end
# application.html.erb
...
<%= yield %>
...
<%= render :partial => 'layouts/footer', :locals => { :recent_users => #recent_users, :recent_posts => #recent_posts } %>
# layouts/_footer.html.erb
<% recent_users.each do |user| %>
<%= link_to user.name, user %>
<% end %>
# same for posts
A few important things to note:
don't access the instance variables (the #foo) in the partial... pass it into the locals hash and access it as a variable instead. It's just generally bad practice
you could also use a module
look into caching because you probably don't want to hit your database TWICE on every page load. You could use fragment caching on the footer and expire it every 15 minutes (probably the best option).

Resources