I have the following snippet of code in my controller
def create
#message = Message.new(params[:message])
#message.message = h(#message.message)
if #message.save
flash[:message] = "Message Sent. Thank You for Contacting Me"
else
flash[:message] = "OOps Something went wrong"
end
redirect_to :action => 'contact'
end
When I try to display the flash message in the contact form it doesnot display. I looked up possible solutions, but they dont seem to work. Any ideas what is going wrong?
Your controller is redirecting to :action => 'contact'. Ensure that the template being rendered for that action has the flash notice output.
<%= flash[:message] %>
Also, you may want to use render :action ... vs redirect_to :action .... Save yourself a request.
The flash hash can contain any set of messages you want to save until the next render. Scaffolds usually use the contents of flash[:notice] for notification messages. If you didn't use a scaffold to generate your web page you will have to add <%= flash[:notice]%> to your views.
You're setting flash[:message] in your controller. So it's not going to show up anywhere in your view unless your view contains <%= flash[:message]%> somewhere.
Your possible solutions are change all occurrences of flash[:message] to flash[:notice] in your controller or add <%= flash[:message]%> to any views that this action could render.
Not saying that you wouldn't have tried it, but if I were you I would do something down the lines like
<% if flash[:messsage].blank? %>
<h1> flash hash is blank </h1>
<% end %>
If you see the "flash hash is blank" in your browser you know what it means.
EDIT:-
Something from the docs "Just remember: They‘ll be gone by the time the next action has been performed." Try this in your controller
flash.keep(:message) #keep the flash entry available for the next action
I recently ran into a scenario where flash messages will not be preserved. This scenario is when submitting a form which does not contain the CSRF token field. Rails will not be able to verify the CSRF token, so it will not preserve the flash messages. If you are using a plain HTML form, use the form_tag helper to automatically add this field for you.
<%= form_tag '/example/url' do %>
<input name="example">
<input type="submit" value="Submit the Form">
<% end %>
Related
I am using Rails 4 and Bootstrap 3 to build a CRUD web app.
I have an Invite model that lets users invite other users to join the app — and this is currently working.
This is my InvitesController:
class InvitesController < ApplicationController
def create
# some code
if #invite.save
# some code
redirect_to some_path, notice: 'Invitation was successfully sent.'
else
redirect_to another_path, alert: 'Invitation could not be sent: please make sure you enter a valid email address and choose a role.'
end
end
end
Now, when a user successfully sends an invite, he is redirected to some path and gets a blue notice telling him precisely that.
However, when a user does not fill the Invite form correctly and the invite cannot be sent successfully, he is redirected to the same view (where the form is) and gets a yellow alert telling him there was an error in his form.
I would like to make the latter a red alert and I thought Bootstrap would allow me to do so by using redirect_to another_path, error: 'Invitation could not be sent: please make sure you enter a valid email address and choose a role.'
This did not work.
–––––
UPDATE: What I am looking for here is a way to use the .alert-danger Bootstrap alert class, I don't want to simply change the color of my alert from yellow to red.
–––––
What is the Rails / Bootstrap way of achieving what I need?
If you are using the twitter-bootstrap-rails gem there is a great helper already available for you to use that pretty much handles what you already have in place.
Gemfile
gem 'twitter-bootstrap-rails'
application.html.erb
<%= bootstrap_flash %>
The bootstrap_flash method is used inside your views and is where the flash will display. I prefer to put mine inside the application.html.erb file as it is used across most of my pages. Furthermore, this method pretty much does what Moustafa posted except the gem creator just did it for you.
Here is the way I accomplish this:
First I create a helper method to translate the flash message type into bootstrap type
def bootstrap_class_for flash_type
case flash_type.to_sym
when :success
"alert-success"
when :error
"alert-danger"
when :alert
"alert-warning"
when :notice
"alert-info"
else
flash_type.to_s
end
end
Then I create a partial to render the messages stored in the flash session according to its type.
<% flash.each do |type, message| %>
<div class="alert <%= bootstrap_class_for(type) %> fade in">
<button class="close" data-dismiss="alert">×</button>
<%= message.html_safe %>
</div>
<% end %>
in common_apps/show, I write an if/else statement that checks if the user has a 'common_app', and if they don't, I redirect them.
In the corresponding common_apps/show view, I just render the common_app.
However, in the case where the user should be redirected, rails is producing the error
nil' is not an ActiveModel-compatible object. It must implement :to_partial_path.
The reason this is happening because #common_app will be nil, if the user does not have a common_app. However, in the common_app controller I do have the if/else statement, so if the common_app is nil, then it should redirect to a different page.
How should I write this so that in the case where a user has no common_app, they are redirected, rather than seeing a nil error?
This is my show definition in the controller :
def show
if current_user.common_app.present?
redirect_to new_common_app, notice: "Looks like you haven't made your common application. Fill it in below."
else
#common_app = current_user.common_app
end
end
This is my show view :
<% provide(:title, current_user.name) %>
<h1><%= current_user.name %></h1>
<ul>
<%= render #common_app %>
</ul>
Your logic on the if condition is the wrong way round. Can be better written as:
def show
#common_app = current_user.common_app
redirect_to new_common_app_path, notice: "Looks like you haven't made your common application. Fill it in below." unless #common_app.present?
end
in your show action why not change to if current_user.common_app.nil? or even if !current_user.common_app
You could do this way if you want to use your #common_app varible in your partial
<%= render partial: "name_of_partial", locals: {common_app: #common_app} %>
I have a strange problem. I've been coding in Rails for, off and on, a year. I created a new project recently and used scaffolding. Things were going fine, yesturday I started implementing some favoriting features. Now I have a strange problem. I rolled back the stuff I did last night but still have the problem. First
Entry belongs to user
User has many entries
My Entry show method in my controller is very standard and simple
def show
#user = User.find(params[:user_id])
#entry = #user.entries.find(params[:id])
respond_to do |format|
format.html # show.html.erb
format.json { render :json => #entry }
end
end
When I view the entry from a normal link in the entries index
<%= link_to 'Show', user_entry_path(#user, entry) %>
I takes me to where it should go:
/users/4/entries/11
When I create new things still look good
/users/4/entries/new
Until I click "create entry" or the submit button
<div class="actions">
<%= f.submit %>
</div>
Then it goes to
/entries/20 ...with the error:
ActiveRecord::RecordNotFound in EntriesController#show
Couldn't find User without an ID
If I go back to the entries index however, the file new entry is there and the show link takes me to the right place. Thoughts? Your help is appreciated!
The error message tells you that User.find(params[:user_id]) couldn't find a user with that ID. Try checking the structure of the GET parameters in the server logs.
If your GET path is /entries/20, then the path only has an entry ID and is missing a user ID. You might be able to fix this in your Controller#create by having it redirect to user_entry_path instead of entry_path.
How does your form look like?
I think you have nested routes? Your form should look like following:
<%= form_for [#user, #entry] do |f| %>
<% # your fields %>
<% end %>
Your form seems to point to resources entry, instead of the nested ressource..
on form submission to "create" action in the sexes_controller, I've changed things to actually redirect back to the page views/results/index that the form was submitted from, but now the flash message is not displaying even though I'm doing
<%= flash[:notice] %>
at the bottom of views/results/index (i.e. the page the form was submitted from), which is how I assume you're supposed to do it.
Is it because there's some sort of caching that's taking place that the flash message is not showing up? Any idea how to get around that?
Update
thinking it might be more complicated, I've tried to retrieve the flash message in the index action of the results controller
#flashbash = Sex.find(params[:id])
and then back in views/results/index
<%= if #flashbash flash[:notice] %> (I think this code is wonky)
note, I tried this, but it didn't work. It said, Couldn't find Sex without an ID
Any ideas how I can fix this?
Typically the flash is rendered in the application's layout file. This avoids the duplication of having to output <%= flash[:notice] %> in every view that could potentially have a flash message.
As to why its not showing up check that you are setting your flash[:notice] variable with something to display. An example of a create action in a controller may look like this:
# app/controllers/sex_controller.rb
def create
#sex = Sex.new(params[:sex])
if #sex.save
flash[:notice] = "Saved successfully"
redirect_to #sex # This redirects to the show action, where the flash will be displayed
else
flash[:error] = "There were errors..."
render :action => :new # This displays the new form again
end
end
# app/layouts/application.html.erb
<html>
...
<%= flash[:notice] %>
<%= flash[:error] %>
<%= yield %>
...
</html>
More information on flash messages here: http://guides.rubyonrails.org/action_controller_overview.html#the-flash
One thing you can do on my rap lyric explanation site is "like" explanations (once you're logged in):
http://dl.getdropbox.com/u/2792776/screenshots/2010-01-17_1645.png
I'd like to show the "Like" links to users who aren't logged in, and then, when a non-logged in user clicks "Like", show him a lightbox with a "Login or Register" form (like Digg / Reddit)
http://dl.getdropbox.com/u/2792776/screenshots/2010-01-17_1650.png
What's the best way to accomplish this?
Currently I'm using this approach:
Clicking "Like" POSTs to /annotations/:id/vote (the POST body indicates whether the user is liking or "unliking").
The vote Annotation controller action has a require_user before_filter that looks like this:
def require_user
unless current_user
store_desired_location
flash[:notice] = "You'll need to login or register to do that"
redirect_to login_path # map.login '/login', :controller => 'user_sessions', :action => 'new'
return false
end
end
user_sessions#new looks like this:
def new
#user_session = UserSession.new
respond_to do |format|
format.html {}
format.js {
render :layout => false
}
end
end
The problem is that the redirect doesn't seem to work correctly over javascript:
http://dl.getdropbox.com/u/2792776/screenshots/2010-01-17_1700.png
How do I get this to redirect correctly?
Also, is this the right general approach? Another thought I had was to attach a different handler to the "Like" links in javascript when there was no logged in user (but I don't think this method scales well to other actions that I'd like to handle the same way)
There's a few problems to overcome here.
Browsers in general do not allow redirecting to a POST request.
redirect_to doesn't preserve format without additional input.
Store location does not preserve form data.
All these problems can be solved by eliminating redirects.
Here is how I've handed it in the past:
Instead of redirecting in required_user, render. If a before filter redirects or renders the pending action is cancelled. (No need to return false either). Unfortunately going this route blurs controller boundaries. But allows for simple html fallback, and lends its self to DRYness.
The high level view of the new work flow will be:
Request to annotations#vote (POST)
required_user filter fails
render new session
submit login information and original POST data back to annotations#vote (POST)
new filter in vote captures session information and logs in. vote proceeds as expected. If login fails return to 3.
annotations#vote redirects/renders as it should
Start by reworking the require_user to render the user_sessions#new template.
def require_user
unless current_user
flash[:notice] = "You'll need to login or register to do that"
#user_session ||= UserSession.new
respond_to do |format|
format.html {render :template => 'user_sessions/new'}
format.js {
render :template => 'user_sessions/new', :layout => false
}
end
end
end
The #user_session ||= UserSession.new ensures we can return validation errors to the form.
Now we've got to beef up your user_session#new template so that it can remember the action. Also if you plan on using lightboxes, this should be a partial rendered rendered by relevant RJS or the new.html.erb.
First we create a partial to create hidden fields preserving the POST data that would have been lost in a redirect:
<% if params[:controller] == "annotations" %>
<% content_for :old_form do %>
<%= hidden_field_tag "annotation[song_id]", params[:annotation][:song_id] %>
<%= hidden_field_tag "annotation[vote]", params[:annotation][:vote] %>
<% end %>
<% end %>
Then render that partial in the login partial that will occupy your lightbox:
<%= render :partial => vote_form_replica %>
<% url = params[:controller] == "user_sessions ? user_sessions_url : {} %>
<% form_tag #user_session, :url => url do |f| %>
<%= yield :old_form %>
<%= f.label :user_name %>
<%= f.text_field :user_name %>
<%= f.label :password %>
<%= f.password_field :password %>
<%= submit_tag %>
<%end%>
The empty hash for url in the form_tag looks like an error, but isn't. It ensures that the form data is posted to the url that rendered the form. Which at this point should be annotations/:id/vote
Now for the new filter to login. Essentially it will be doing what ever UserSessionsController#create does without the render/redirect. The following is copied from the RESTful authentication plugin.
def authenticate
self.current_user = User.authenticate(params[:login], params[:password])
if logged_in?
if params[:remember_me] == "1"
current_user.remember_me unless current_user.remember_token?
cookies[:auth_token] = { :value => self.current_user.remember_token,
:expires => self.current_user.remember_token_expires_at }
end
end
end
All that's left is to make sure the filter order is right.
before_filter :authenticate, :require_user, :only => :vote
N.B.: You're probably not going to use this version of require_user without this version of authenticate so it makes sense to combine them into a single filter.
And that's it. The way this has been set up allows for robust DRY easily reuseable code. By placing the new filters into ApplicationController they're available in any controller. From this point, adding this functionality to any other controllers/actions takes only 3 simple steps:
Create a new partial modelled after the vote_form_replica partial.
Add the corresponding render statement to the new session template.
Apply the filters to your actions.
I would approach this in the way you describe at the bottom of your question. Before displaying the page initially, check if the user is logged in. If they are, the "Like" links should use their normal behavior. If not, bind a click event to show the register/login panel. There's nothing about this that can't be reused. In fact, we use this exact method at my job. Any user action that requires authentication either follows its normal behavior or pops up a generic login panel depending on login state at the time the page loads.