Here are the actions for my messages controller:
def new
#message = Message.new
end
def create
#message = Message.new(message_params)
if #message.valid?
Contactform.contact(#message.name, #message.town, #message.email, #message.content).deliver
redirect_to 'messages#new', flash: { success: "Sent Message" }
else
redirect_to 'messages#new', flash: { alert: "Message Error" }
end
end
This is what I want to happen:
I go to the 'new' route, a #message instance is created, and in the 'new' view there is a form where we can update this 'Message' instance' attributes.
Now, I'm following a tutorial, but it seems a little work-aroundy. When we 'submit' the form, we are automatically taken to the 'create' action, and a second message instance is created and populated with the old instances attributes.
If the attributes pass the Message model's validations, the attributes are passed into the 'Contactform' mailer's 'contact' action, which delivers the message. We are then redirected to the 'new' view, and the flash is populated with a "Sent Message" string, that we can see when we arrive at the 'new' view.
If the attributes fail the Message model's validations, we are then redirected to the 'new' view, and the flash is populated with a "Message Error" string, that we can see when we arrive at the 'new' view.
What actually happens
Everything works perfectly, apart from the flash. Once it's populated with either the 'success' or 'alert' message, it doesn't change until I browse to another page. Then it gets the updated message, on a new page, so I think because I'm coming back to the same location, the flash isn't updated for some reason.
How can I get the flash to behave normally, despite us coming back to the same page?
Update
Just to say again, it works perfectly if I get redirected to another page. Just because it's the same page, the flash doesn't get updated.
Also, I now have this in the view: Still doesn't do anything.
<% if !flash.empty? %>
<% flash.each do |index, value| %>
<div class = "alert alert-<%= index %>"> <%= value %></div>
<% end %>
<% flash[:error] = flash[:message] = flash[:notice] = nil %>
<% end %>
If you want the flash to show up within the same request (i.e. without a redirect taking place), you'll need to use flash.now. Here's the explanation in the rails docs:
By default, adding values to the flash will make them available to the next request, but sometimes you may want to access those values in the same request. For example, if the create action fails to save a resource and you render the new template directly, that's not going to result in a new request, but you may still want to display a message using the flash
See section 5.2.1 http://guides.rubyonrails.org/action_controller_overview.html
Related
I'm having difficulty getting my notices to display in my Rails 5 view. I have this before_filter method set up in a controller:
def check_email_confirmed!
redirect_to controller: "users", action: "edit", notice: 'Please confirm your email.' unless current_user.email_confirmed
end
and in my view I have this
<% if flash[:notice] %>
<p class="flash-notice"><%= flash[:notice] %></p>
<% end %>
but despite the fact that I know the notice is getting set (I see it in my URL) the above is not getting invoked in my view (I see no HTML with the class="flash-notice").
Is there some other way I should be setting flash notices in a redirect? Or should I be using a redirect at all (someone told me there might be some security risks in embedding messages in the URL query string)?
You're currently setting a parameter with the key notice, not setting the flash in your session.
To accomplish this the way you're doing it you would have to do:
<p class="flash-notice"><%= params[:notice] %></p>
Most Rails apps that I've worked on set the session[:flash] in the controller method, in which case you would do:
unless current_user.email_confirmed
redirect_to edit_user_path(current_user)
flash[:notice] = 'Please confirm your email.'
end
Unless you have a good reason to pass the notice text as a URL param, I'd recommend doing it this way.
Try
flash[:notice] = 'bla'
redirect_to controller: "users", action: "edit"
I'm pretty sure that
redirect_to edit_user_path(user), notice: 'bla'
Will also work. For some reason your syntax apparently doesn't pick up the notice modifier. But you'll have to assign user to your current user for that to work obviously.
def show
email_address = EmailAddress.find_by(confirmation_token: params[:id])
if email_address.confirm!
flash[:notice] = "Thanks for confirming your email address (#{email_address.email})"
flash.keep
redirect_to user_account_path
else
flash[:error] = "Invalid confirmation token"
redirect_to root_path
end
end
Trying to redirect from this controller to another controller and i want to flash "Thanks for confirming your email address (#{email_address.email})" but am not seeing the flash message on the page am redirecting to.
Tried hardcoding a flash message on the other controller and it got displayed.
It's only when I do a redirect the flash message doesn't go through.
You can use redirect_to and flash together:
redirect_to user_account_path, notice: "Thanks for confirming your email address (#{email_address.email})"
Try to use this way to make redirect and show flash notice.
If this is doesn't work then probably your problem related to rendering flash message. Check content of flash helper in view and code which render it.
You might be missing flash code in layout.
Add the following in layouts/application.html.erb in body section:
<% flash.each do |name, msg| -%>
<%= content_tag :div, msg, class: name %>
<% end -%>
I have a simple Rails application where I create objects (such as posts). So far I can edit and delete them, one by one, but now I want to have a <%= notice %> echoing the name of the deleted object after confirming its deletion. Is this possible? If so, how?
This is an extremely common task in Rails, and the idiomatic solution is to forward some data about the deleted record to the subsequent GET request via the the flash array.
Your controller's destroy action should look something like this:
def destroy
#post = Post.find(params[:id])
#post.destroy
redirect_to posts_path, notice: "#{#post.name} was deleted"
end
In your index action, you'll be able to access flash[:notice] to get the string generated in the previous action.
You need to store the details you want to echo (e.g. the name) somewhere, because the object itself will be gone after a redirect. I would use the flash for that:
# in the controller
def destroy
thing = Thing.find(params[:id])
thing.destroy
redirect_to things_path, :notice => "Thing #{thing.name} was deleted"
end
# in the index view
<% if flash[:notice] %>
<div class="notice"><%= flash[:notice] %></div>
<% end %>
When I save a new user the notice fires twice. Any suggestions on how to get it to fire once? I am also using Sorcery
def create
#user = User.new(user_params)
if #user.save
redirect_to #user, notice: 'Profile successfully created.'
auto_login(#user)
else
render :new
end
end
As per the code snippet given above, I don't think notice will appear twice. There is nothing wrong in your code. There is one possible chance that in your application layout displaying notice is defined once and in your current view template it's defined again. That's why it's showing twice.
Flash:
The flash provides a way to pass temporary objects between actions. Anything you place in the flash will be exposed to the very next action and then cleared out.
A notice can't fire twice according to your code. In your app you somewhere have this code twice (my guess would be in your layout file and then your view)
<% flash.each do |name, msg| %>
<%= content_tag :div, msg, :id => "flash_#{name}" %>
<% end %>
For the code below, what happens if replacing redirect_to with render or vise verse?
def create
#product = Product.new(params[:product])
respond_to do |format|
if #product.save
format.html { redirect_to(#product, :notice => 'Product was successfully created.') }
else
format.html { render :action => "new" }
end
end
end
It seems OK replacing one with the other in code above. Is there a place where only redirect_to or render has to be used? Render does nothing but rendering a view. Redirect_to sends 302 request to server and current parameters are lost after redirecting.
Thanks.
If you're using render, when the user refreshes the page, it will submit the previous POST request again. This may cause undesired results like duplicate purchase and others.
But if you're using redirect_to, when the user refreshes the page, it will just request that same page again. This is also known as the Post/Redirect/Get (PRG) pattern.
So the place where redirect_to should be used is when you're doing a HTTP POST request and you don't want the user to resubmit the request when it's done (which may cause duplicate items and other problems).
In Rails, when a model fails to be saved, render is used to redisplay the form with the same entries that was filled previously. This is simpler because if you use redirect, you'll have to pass the form entries either using parameters or session. The side effect is that if you refresh the browser, it will try to resubmit the previous form entries. This is acceptable because it will probably fail the same way, or if it's successful now, it was what the user should expect in the first place anyway.
For more in depth explanation about render and redirect, you should read this article.
When you redirect you will generate a new request that hits a controller method, render just renders the associated view. You use render in the create because you want to keep the state of the model object if the save fails so that you can render info about its errors. If you tried to redirect to the new_product path you would create a new model object and loose all the form data the user entered and any errors etc etc
EDIT (with some more info):
An example of a situation where you MUST use redirect_to is if your view template uses instance variables that are not initialized in the controller method you are redirecting from. So you probably could not call render {:action => 'index'} in your create method because the index template probably makes use of a #products variable but your only initialized #product so it would cause an exception
Here is a complete list of what the two methods do that I follow:
1) redirect_to will issue an HTTP 302 status code by default. A 302 redirect is a temporary change and redirects users and search engines to the desired page for a limited amount of time until it is removed. You can optionally specifiy a 301 status code to redirect_to. A 301 status code is used when any page has been permanently moved to another location. Users will now see the new page as it has replaced the old page. This will change the URL of the page when it shows in search engine results.
2) redirect_to will issue a new HTTP request, since it is redirects to a different controller action or URL. You should not make the browser need to make a fresh call unless you really have to, so always question when you are using redirect_to and if it is the right thing, or perhaps a render would be better.
- redirect_to will cause any automatic template rendering of the current action to be skipped.
3) render will issue an HTTP 200 status code by default ( but with an invalid ActiveRecord object, you may want to change this to 422 unprocessable entity). The HTTP 200 OK success status response code indicates that the request has succeeded. The 422 (Unprocessable Entity) status code means the server understands the content type of the request entity nd the syntax of the request entity is correct but was unable to process the contained instructions.
4) render will render a template and any instance variables defined in the controller action will be available in the template. Of course, instance variables will not be available if the subsequent action that redirect_to invokes. IMPORTANT POINT: Redirect hits the controller while Render does not, so if you render a different template, it will not hit the action associated with that template and so those instance variables will not be available!
5) With render, use flash.now, instead of the normal flash.
flash.now[:error] = "There was a problem"
# not
flash[:error] = "There was a problem"
6) If you don't, then the flash message may not show up on the page that's rendered, and it will show up on the next page that's visited.
7) render will not cause the current action to stop executing! redirect_to will not cause the current action to stop executing! You need to invoke 'return' if you need to bypass further execution of code in the action! In the below code, there is an explicit render at the bottom and so you must do a return to avoid an error of redirect and render both being present:
def update
#record = Record.new(record_params)
if #record.save
flash[:success] = "record was successfully saved"
redirect_to records_path
return
end
flash.now[:error] = "please fix the problems in the record"
render :edit
end
Another option:
def update
#record = Record.new(record_params)
if #record.save
flash[:success] = "record was successfully saved"
redirect_to records_path
else
flash.now[:error] = "please fix the problems in the record"
render :edit
end
end
8) The flash message provides a way to pass temporary primitive-types (String, Array, Hash) between actions. Anything you place in the flash will be exposed to the very next action and then cleared out. This is a great way of doing notices and alerts:
class PostsController < ActionController::Base
def create
# save post
flash[:notice] = "Post successfully created"
redirect_to #post
end
def show
# doesn't need to assign the flash notice to the template, that's done automatically
end
end
show.html.erb
<% if flash[:notice] %>
<div class="notice"><%= flash[:notice] %></div>
<% end %>
Since you can have both notices and alerts in the flash, you can display both notices and alerts this way:
<% flash.each do |key, value| %>
<%= content_tag :div, value, class: "flash #{key}" %>
<% end %>