I don't like the way rails does page titles by default (just uses the controller name), so I'm working on a new way of doing it like so:
application controller:
def page_title
"Default Title Here"
end
posts controller:
def page_title
"Awesome Posts"
end
application layout:
<title><%=controller.page_title%></title>
It works well because if I don't have a page_title method in whatever controller I'm currently using it falls back to the default in the application controller. But what if in my users controller I want it to return "Signup" for the "new" action, but fall back for any other action? Is there a way to do that?
Secondly, does anyone else have any other ways of doing page titles in rails?
I disagree with the other answers, I believe the title shouldn't be set per action, but rather within the view itself. Keep the view logic within the view and the controller logic within the controller.
Inside your application_helper.rb add:
def title(page_title)
content_for(:title) { page_title }
end
Then to insert it into your <title>:
<title><%= content_for?(:title) ? content_for(:title) : "Default Title" %></title>
So when you are in your views, you have access to all instance variables set from the controller and you can set it there. It keeps the clutter out of the controller as well.
<%- title "Reading #{#post.name}" %>
I like to put a catchall, default title in my layout that can be overridden from an action by setting #title:
<title><%= #title || "Default Title Here" %></title>
Then you can generate a title in your action:
def show
#post = Post.find_by_id params[:id]
#title = "tybro's blog: #{#post.title}"
end
I would do this:
# Application Controller
before_filter :set_page_title
private
def set_page_title
#page_title = "Default Title"
end
overwrite it in your other controllers
# Users Controller
before_filter :set_page_title
def new # in Users controller
...
#page_title = "Sign up"
...
end
private
def set_page_title
#page_title = "Users"
end
In your view:
<title><%= h #page_title %></title>
Have a look at Ryan Bates (ryanb from railscasts.com) nifty layout. He has a method in there that does what you are looking for. It's similar to Garrett's way, only he moves the default values in the helper too. Check out the helper.rb file and how he link textuses it.
You can install is as gem (and use the other nice features) or just use his way of doing it. You only need to pass the title value to title in each view (or let it fall to the default) and there you go. I'm with Garrett to put these things in the view.
Layout template
In your layout (e.g. application.html.erb) add:
<title><%= content_for(:page_title) || "Fallback title" %></title>
Page template
In the page template you'd like to a specify a title from:
<%- content_for(:page_title, "Specific page title") %>
class ApplicationController < ActionController::Base
before_action :set_page_title
private
def set_page_title
#page_title = t(".page_title", default: '').presence || t("#{controller_name}.page_title", default: '').presence || controller_name.titleize
end
end
I recently started taking this approach then outputting #page_title in the layout. It seems to work quite nicely for me.
Related
I am building a Rails project where I have a user, and that user has many Tests (it's like a Trivia game). I have a UsersController where I query the user (going to implement login later).
In my view I have a button which "starts" the test. I need to associate the user with a test, because my User has_many Tests (user_id is a foreign key on tests). My question is, how do I pass my #user object to my TestsController so I can associate the created test with the logged in user?
Here is my UsersController:
class UsersController < ApplicationController
def show
#user = User.find(1)
end
def start_test
redirect_to tests_path
end
end
In my users show view I have:
<p>Welcome <%= #user.name %>!</p>
<%= button_to "Start Test", users_start_test_path %>
When the button is clicked I redirect to tests_path which is in TestsController:
class TestsController < ApplicationController
def index
# here I need to create the Test belonging to the user
end
end
I am new to Rails and don't know how to pass that #user to TestsController so I can create the test belonging to the user. Theoretically I don't even need the whole #user, just the id. Any help would be greatly appreciated.
I think you should improve this by setting the route properly, Instead linking a controller action directly to another controller. I would create the following route.
resources :users do
resources :tests
end
This will force you to always have a user_id. For your new tests path you get this. new_user_test_path(#user)
This will use basics CRUD actions. So to make a new test for the correct user, use the the def new action in the tests controller.
def new
#user = User.find(params[:user_id]
#new_test = #user.tests.new
end
The controllers are standalone. They cann't share variables. You could try to send user id intro the request to controller TestsController (or better use an authentication system). Or directly in the TestsController calls User.find(1) again.
The only variable you can pass between controllers is a parameter through the routing (or the session).
So you can have a HTML Form that passes a body to an action inside a different controller OR for what I see in your case by visiting a route and pass a friendly query string parameter, like an ID.
So you could:
<%= button_to "Start Test", users_start_test_path(user_id: #user.id) %>
And then in your TestController:
class TestsController < ApplicationController
def index
#user = User.find params[:user_id]
end
end
I don't like to pass the current_user variable because it can then be hijacked.
This is how I would set the TestsController
class TestsController < ApplicationController
def index
#tests = current_user.tests
end
def new
#test = current_user.tests.new
end
def create
current_user.tests.create(test_params)
end
def show
#test = current_user.tests.find(params[:id]) # this will only find tests scoped to that user
end
def edit
#test = current_user.tests.find(params[:id]) # this will only find tests scoped to that user
end
def update
#test = current_user.tests.find(params[:id]) # this will only find tests scoped to that user
#test.update(test_params)
end
def test_params
params.require(:test).permit()
end
end
This way now you have the test scoped to the user.
I am assuming that you have device for user management
One of my model objects has a 'text' column that contains the full HTML of a web page.
I'd like to write a controller action that simply returns this HTML directly from the controller rather than passing it through the .erb templates like the rest of the actions on the controller.
My first thought was to pull this action into a new controller and make a custom .erb template with an empty layout, and just <%= modelObject.htmlContent %> in the template - but I wondered if there were a better way to do this in Rails.
In your controller respond_to block, you can use:
render :text => #model_object.html_content
or:
render :inline => "<%= #model_object.html_content %>"
So, something like:
def show
#model_object = ModelObject.find(params[:id])
respond_to do |format|
format.html { render :text => #model_object.html_content }
end
end
In latest Rails (4.1.x), at least, this is much simpler than the accepted answer:
def show
render html: '<div>html goes here</div>'.html_safe
end
Its works for me
def show
#model_object = ModelObject.find(params[:id])
respond_to do |format|
format.html { render :inline => "<%== #model_object['html'] %>" }
end
end
I want to make an Navigation with specific Tags.
These Tags are for example: HTML, CSS and Javascript.
So when i click on one of them it will show all posts with these tag.
How can i achieve that?
My code for the Navigation right now looks like this (it's in the Application.html.erb)
<%= link_to "Computer", tag_list.Computer %>
I get this Error:
undefined local variable or method `tag_list' for #<#:0x007feec764ff88>
tag_list is a local variable or method, so unless you've created it in a helper that's your first issue. The second is that called .Computer on it doesn't work because tag_list is a method that created by the gem to list all an objects tags, and calling the . (also knowing as chaining) is attempting to call a method named Computer, which doesn't exist, that should just be a string and strings have to be quoted.
So, in your layout view, you can do
= link_to "Computer", tagged_posts_url(tag: "Computer")
Then in your posts_controller.rb add an action called tagged
def tagged
if params[:tag].present?
#posts = Post.tagged_with(params[:tag])
else
#posts = Post.all
end
end
To maintain a DRY set of views, you can even tell it to render the index view since you most likely already have a list of posts, now it will look exactly the same but only contain posts with that tag. e.g.
def tagged
if params[:tag].present?
#posts = Post.tagged_with(params[:tag])
else
#posts = Post.all
end
render "index"
end
Then in your config/routes.rb add a route for your new controller action under your existing post route
resources :posts do
collection do
get "/posts/tagged", as: :tagged
end
I got it myself.
Here is the Code:
<%= link_to 'Computer', { :controller => 'posts', :action => 'index', :tag => 'Computer'} %>
The controller looks like this:
def index
if params[:tag]
#posts = Post.tagged_with(params[:tag]).order('created_at DESC')
else
#posts = Post.all.order('created_at DESC')
end
end
I'm trying to find a way to access html elements from my view from within my model.
I'm trying to access the the page title. On my view I have this:
<% provide(:title, 'Baseline') %>
And from my model, here is my latest attempt:
def steps
if #title == 'Baseline'
%w[sfmfa phq whoqol_bref positive_negative ]
elsif #title == 'Treatment Completion'
%w[smfa phq ]
else
%w[]
end
end
I also tried by using params[:title], but params isn't recognized in the model. This feels like a really dumb question, but I haven't been able to find a straight forward answer.
Any input would be greatly appreciated.
EDIT. Adding more detail.
As mentioned below, I'm going about this wrong. So now I'm trying to pass the correct identifier from my controller, to the model.
Currently I have pagination for one page, 'Baseline'. I'm trying to allow for pagination on 2 other pages. I simply need to be able to change the value of whats held in steps.
My old 'steps' method looked like this:
subject.rb
def steps
%w[sfmfa ...]
end
And here are what steps is used for:
def current_step
#current_step || steps.first
end
def next_step
self.current_step = steps[steps.index(current_step)+1]
end
def previous_step
self.current_step = steps[steps.index(current_step)-1]
end
def first_step?
current_step == steps.first
end
def last_step?
current_step == steps.last
end
So, I guess my new method might look something like this, where I pass the argument from the controller:
def steps(title)
if title == 'Baseline'
%w[sfmfa ...]
elsif title == 'Other'
%w[sma ...]
else
#Shouldnt get here
end
Also, here is how my view renders the steps:
<%= render "base_#{#subject.current_step}", :f => f %>
I have the following code in my controller:
class TestController < ApplicationController
##a = 1
def index
#temp = connection.execute("select test_id from mastertest limit #{##a}, 5;")
end
And I have the following code in my View(Html.erb) File:
<button type="submit" value="Next" form="submit_form">NEXT</button>
<form id="submit_form">
<% ##a = ##a + 1 %>
<table>
<% #temp.each do |row| %>
<tr><td><%= row[0] %></td></tr>
<% end %>
</table>
</form>
So basically I am trying to change the value of the class variable ##a on clicking the Next button. But it does not change the value of ##aa. Can someone help me how to do that.
Did you try using helper method?
module ApplicationHelper
##a = 1
def increment_a
##a = ##a + 1
end
end
and in your view just call;
<% increment_a %>
Not that the ## variable is a class variable and it's shared among all instances of the that class. So define that class somewhere in the ApplicationHelper class and then it will be shared and can be accessed in the Controllers and views.
In all cases I highly discourage using class variables in such a way and recommend that you ind another way to share data/variables between view / controller. Maybe use another supporting class or store values in the database.
If you want to alter a Rails variable on a form submission, you should put the code to do it in the action which processes the form.
As you've written it, I believe the variable will get set when the template containing the form is rendered.
I also vaguely recall that there's some special considerations about class variables in Rails apps. You should look into that and make sure you're using a technique that won't cause any unexpected results.
Ok I managed to fix this:
Ruby has something called a global variable which can be declared like this :
$a = 1
Using $a everywhere retains its value in the controller and the view as well.