View application.html.erb contains following content
<title><%= content_for?(:title) ? yield(:title) : t(:stocktaking_title) %></title>
How can I pass data to this view from the controller's method?
I mean use symbol :title. I'm don't know Ruby well.
class WelcomeController < ApplicationController
def download
view_context.content_for(:title, "My Awesome Title") # doesn't work :(
end
end
Rails 4.1.9, ruby 2.0.0 (2014-05-08) [universal.x86_64-darwin14]
The prependeded # character to a variable is what exposes the variable to the view scope. In your controller:
def show
#title = "My Title"
end
Will let any rendered template file access it using:
<%= #title %>
Clearly, you're looking for some sort of title processing logic. Perhaps you could try replacing the code in your application.html.erb file with something like:
<% if #title %>
<title><%= #title %></title>
<% elsif content_for?(:title) %>
<title><%= yield(:title) %></title>
<% else %>
<title><%= t(:stocktaking_title) %></title>
<% end %>
You could condense this into a ternary but the view wouldn't be very readable.
If you insist on using content_for inside of the controller, you can use the view_context method, but you can't seem to work with content_for directly like so:
view_context.content_for(:title, "My Awesome Title")
Instead, you'll need to implement your own content_for method to extend off of the view_context. I pulled it from this Gist, but here's the code:
class ApplicationController < ActionController::Base
...
# FORCE to implement content_for in controller
def view_context
super.tap do |view|
(#_content_for || {}).each do |name,content|
view.content_for name, content
end
end
end
def content_for(name, content) # no blocks allowed yet
#_content_for ||= {}
if #_content_for[name].respond_to?(:<<)
#_content_for[name] << content
else
#_content_for[name] = content
end
end
def content_for?(name)
#_content_for[name].present?
end
end
This has been tested and works.
Then just do content_for :title, "My Awesome Title" in your controller.
Seriously though, using #title will be way way easier and less "hacky." You could even do something cool like this:
<title><%= #title || content_for(:title) || t(:stocktaking_title) %></title>
Related
I am stuck at what I think is a very simple/common usecase in a Rails web application. I want to use "caches_action, layout:false" and display, from the layout, dynamic tags that will be set by the action (either from the view or the controller).
I could not find any standard rails way to do this as content_for does not work with caches_action, instance variables are not cached (?), and the metatags helper gems that I have tried (metamagic and meta-tags) do not support this usecase.
Is there any way to do this ?
Example
I am using caches_action, layout:false on a SandboxController#show method
#app/controllers/sandbox_controller.rb
class SandboxController < ApplicationController
caches_action :show, layout: false, expires_in: 1.minute
def show
#meta_title = "Best page ever"
do_some_expensive_operation
end
end
The view
#app/views/sandbox/show.html.erb
We are in show action.
The layout
#app/views/layouts/application.html.erb
<title><%= #meta_title %></title>
Debug: <%= #meta_title %> <br/>
<%= yield %>
Thanks !
I found a way to make it work, it's not as pretty as I would like it to be but it helps using caches_action and setting HTML meta tags from the view.
Also, for the record, it seems that this was forgotten and buried deep down in the pipeline, as I did not find any recent mentions of this problem, only that caches_action and content_for together are not expected to work.
Solution: I simply add a before_action to set the meta tags by using as less computation as possible.
#app/controllers/sandbox_controller.rb
class SandboxController < ApplicationController
caches_action :show, layout: false, expires_in: 1.minute
before_action :seo_show, only: :show
def seo_show
#meta_title = "Best page ever"
end
def show
do_some_expensive_operation
end
end
It's worth noting that it can be used in combination with metamagic gem too.
Layout:
#app/views/layouts/application.html.erb
<%= default_meta_tags && metamagic %>
<%= yield %>
And helper:
#app/helpers/application_helper.rb
module ApplicationHelper
def default_meta_tags
meta title: #meta_title || "Default meta-title of my website"
end
end
Hope this helps someone out there !
I generated a Rails application, and am playing around with the internals. Previously my application.html.erb was rendering properly, but now it seems like Rails is totally ignoring it because it won't even generate an error.
There have been a bunch of questions on Stack Overflow regarding this problem. I've looked at what I think is all of them, but none have helped.
My routes:
Rails.application.routes.draw do
# static_pages from rails tutorial ch. 3
get 'static_pages/home'
get 'static_pages/help'
get 'static_pages/about'
end
Here is the views/layout/application.html.erb
<!DOCTYPE html>
<html>
<head>
<title>This Title is not showing up</title>
<%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track' => true %>
<%= javascript_include_tag 'application', 'data-turbolinks-track' => true %>
<%= csrf_meta_tags %>
</head>
<body>
<p> why isnt this showing up?? </p>
<%= yield %>
</body>
</html>
Here is the static_pages_controller:
class StaticPagesController < ApplicationController
layout 'application' #<- I know this shouldn't be necessary, but I thought i'd try
def initialize
#locals = {:page_title => 'Default'}
end
def about
#locals[:page_title] = 'About'
render #locals
end
def help
#locals[:page_title] = 'Help'
render #locals
end
def home
#locals[:page_title] = 'Home'
render #locals
end
end
Here is the Application Controller:
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
end
There are no other layouts. My Views folder has the following structure:
-Views
|-layouts
||-application.html.erb
|
|-static_pages
||-about.html.erb
||-home.html.erb
||-help.html.erb
I've tried purposefully generating an error in the application.html.erb, calling variables that don't exist and whatever other shenanigans. Rails is totally ignoring me and I'm feeling insecure.
All I wanted to do is to display the page name in the <title>, but I can't even get plaintext to render correctly. How can I get this to work so that I can properly fail at getting the controller variable in the title?
You should not override the controller initialize method. Doing this will break the base class behavior.
While, I believe, just calling the super from the initialize will fix your issue, the correct Rails way to initialize a controller for a specific action is to use a before filter instead.
Example:
class StaticPagesController < ApplicationController
layout 'application'
before_action :load_locals
def load_locals
#locals = {:page_title => 'Default'}
end
...
end
I tried to create a helper module to be able to set the title of a page. Of course it's not working (reference) Is there something I must define in a controller for my helpers methods to be seen by my controllers??
Gitlink: works_controller.rb
def index
set_title("Morning Harwood")
#works = Work.all
respond_to do |format|
format.html # index.html.erb
format.json { render json: #works}
end
end
In application_helper.rb:
module ApplicationHelper
def set_title(title = "Default title")
content_for :title, title
end
end
In the layout work.html.erb:
<%= content_for?(:title) ? content_for(:title) : 'This is a default title' %>
Helpers in Rails are methods available in views (and controllers if you include them) that allow you to avoid code repetition in views.
An example of a helper from my code is a method that renders html for facebook login button. This button is in reality more than user sees, because it's a hidden form with some additional information, etc. For this reason I wanted to make a helper method out of it, so instead of copying 10 lines of code multiple times I can call a single method. This is more DRY.
Now, back to your example, you want to do two things
display page <title>,
add <h1> header at the top of the page.
I see now where linked answer wasn't clear enough. You indeed need helper, but you also need to call it! So
# application_helper.rb
def set_title(title = "Default title")
content_for :title, title
end
# some_controller.rb
helper :application
def index
set_title("Morning Harwood")
end
And then in layout's views you can use:
<title> <%= content_for?(:title) ? content_for(:title) : 'This is a default title' %><</title>
...
<h1><%= content_for?(:title) ? content_for(:title) : 'This is a default title' %></h1>
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
I was wondering how I might echo out the current high_voltage page name as class for my <body>?
My current solution is a bit cumbersome:
# app/views/layouts/application.html.erb
<body class="<%= yield :body_class %>">
# app/views/pages/principles.html.erb
<% content_for :body_class do %>home<% end %>
Any help would be greatly appreciated.
You can create a view helper that will return a string with controller name, action, id, etc.
module ApplicationHelper
def body_class
"#{controller_name} #{controller_name}-#{controller.action_name} #{page_id}"
end
def controller_name
controller.controller_path.gsub('/','-')
end
def page_id
params['id']
end
end
#harlow
Nice solution, though I find it exposes to much information about the guts of my Rails app (that is the usage of 'high_voltage').
Also I don't need the params['id'] outside the context of 'high_voltage' pages contained in the class(es) for the body- or in my case html-tag.
I use this one:
module ApplicationHelper
def html_class
"#{controller.controller_name} #{controller.action_name} #{page_id}"
end
def page_id
return params['id'].gsub('/','-') if controller.controller_name == 'pages'
return nil
end
end