I have a weird problem with flash messages not showing up in IE (tried 8 and 9):
it always works with other browser
the problem is only on one page (this page renders different forms based on a parameter)
the flash message always appears on development, but only sometimes on staging and prod
I see the flash message being logged in all cases, [notice] Your changes have been saved. Next step..., even when it doesn't appear on the page!
The error flash message always shows up, it's the notice that doesn't work properly.
Here's my update action:
def update
#form = Forms::Events::EditForm.build_for(#event, params[:event])
if #form.save
redirect_to edit_challenge_path(#form.event, form: #form.event_name), notice: "Your changes have been saved. #{#form.next_form}".html_safe
else
flash.now[:alert] = "Please correct the errors highlighted below."
render "groups/events/edit"
end
end
Any ideas on what could be wrong?
Agreeing with #AnthonyAlberto's comment. What you want is the .now method of flash, e.g. flash.now[:alert] = ... Here's a good explanation of the difference.
Related
I am trying a simple download from a server. This is using Rails 5.01, Ruby 2.24p230.
The view has a link to the controller to download data. It gets there just fine.
The controller method is just this:
def download
send_data("some sample text", filename:sample.txt)
flash[:success] = "it worked"
end
The result is that a file is named sample.txt containing the correct text is downloaded to the client. flash never happens. The view which linked to the controller is still on the screen, without any page refresh. A view called "download.html.erb" is never called.
My questions are:
Is there a simple way to cause some communication with the client following the send_data? It would be nice to tell the person on the client something after a successful download.
After the send_data, what should happen?
Thank you for taking the time to answer this.
To understand what is going on here you have to understand how the session and flashes work.
A flash message is stored in the session and carried on to the next request. When the request is finished flash messages from the previous request are removed from the "flash hash" which is stored in the session.
class TestController
# GET /foo
def foo
flash[:notice] = "Hello"
redirect_to '/bar'
end
# GET /bar
def bar
flash.now[:alert] = " world!"
end
end
So when the user requests /foo they are redirected to /bar and the flash hash will contain:
{
notice: "Hello",
alert: " world!"
}
So how is this relevant? When the client clicks the download button the flash message you set will be seen in the next request they perform. Which is not really that useful. Instead what you want to do is probably just use javascript and display a popup or some sort of message when the user clicks the download link.
When you send data to the client few will actually allow you to set any kind of redirect to be followed or will ignore any headers you send. This is because the huge number of potential annoying or malicious uses.
I have an app in which the back end is rails, and the front end is built within angular. I am in the process of editing my tokens controller that works on username and password errors. In this controller is a method that creates all of the magic.
The problem that I am having is that within the fail messages, I am trying to create a link within the second half of my fail message. Yet i've been having some problems with it.
def create
username = params[:username]
password = params[:password]
user = User.find_for_authentication(login: username)
fail!(message: 'Account not found. Please try again with new information or #{link_to(new_patient_registration_path)} click here to create a new account', status: :unauthorized) unless user
With what I have right now, I'm unable to have the message process. I wind up the entire error message being displayed, but the without the link.
Would anybody be able to take a quick gander at this for me to see what I am missing out on?
Much thanks in advance!
You need to use view_context
fail!(message: "Account not found. Please try again with new information or #{ view_context.link_to("Click here to create a new account", new_patient_registration_path)}".html_safe , status: :unauthorized) unless user
So I'm trying to display a flash after multiple redirects, but I can't get it to "keep"! I tested out the code in my view file, and that's solid. What am I missing?
if response["status"] == "NotProcessed"
redirect_to root_path, alert: "Does this work?"
That takes me back tot he root path, but no flash is displayed, so I added
flash.keep, and still nothing.
I even added:
def redirect_to(*args)
flash.keep
super
end
to my application controller... but nothing!
Thanks!
Fixed!
I had set protect_from_forgery with: :null_session in my ApplicationController, thus it removed the flash with each session. I removed that and it works fine.
I must have inserted that early on in the application when dealing with a weather API.
Here is my create action. It creates a new instance of Message, which is checked with model validations.
Then there is a simple if else loop that sends the message if the model validations were fulfilled and renders the 'new' view if they want to send another. If the model validations weren't fulfilled, it simply renders the 'new' view again.
The flash hash is also populated with relevant information, a success message and an alert message if the message was successful or not.
def create
#message = Message.new(message_params)
if #message.valid?
Contactform.contact(#message.name, #message.town, #message.email, #message.content).deliver
flash[:success] = "Sent message"
render 'new'
else
flash[:alert] = "Alert"
render 'new'
end
end
However, I don't think I'm doing it right, because the messages stay in the flash. They aren't cleared after they've been viewed once.
How should I ensure that the flash is cleared, as it should be?
Update
This is how the error comes about. I send a message successfully, and I'm routed back to the 'new' view and I receive a 'success' flash. I attempt to send an erroneous message, and I'm routed back to the 'new' view and I receive both the 'success' flash from before, and a 'alert' flash. Very confusing for the user!
The second I browse to another page, both flashes disappear, but that's the problem I think. Something to do with coming back to the same page so Rails doesn't know to clear the flash. Can I force clear the flash?
I think reading the section about flash.now in the rails doc will help clear this up for you: http://guides.rubyonrails.org/action_controller_overview.html#flash-now.
Flash sticks around for the next request. In this case, you aren't issuing a new request after your create action - you are just re-rendering the new view. I think flash.now is what you want to be using here.
I would also say that in your create success case, the create action should be doing a redirect rather than just re-rendering the view. Are redirect_to and render exchangeable? explains the difference between redirecting and rendering
I'm not sure how to check flash messages with functional tests. My problem has something to do with redirect_to because my test for flash[:error] DOES pass. Here is what my create action looks like:
def create
#user = User.new(params[:user])
if #user.save
redirect_to #user, flash: { success: "Welcome!" }
else
flash.now[:error] = "Signup failed:"
render 'new'
end
end
I have a passing test for flash[:error] that 1) checks that flash[:error] is the correct value, 2) checks the page for the flash div, and 3) gets a new page and checks that the flash message has been cleared. I tried to make an analogous test for flash[:success] but it is failing when it tries to find the div on the page. Here is the failing test:
should "show flash[:success] message" do
post :create, user: { name: "valid user",
password: "userpw",
password_confirmation: "userpw",
email: "a#b.com" }
assert_redirected_to user_path(assigns(:user)), "user isn't being redirected to their page after registration"
assert_equal flash[:success], "Welcome!", "flash[:success] isn't being set properly"
assert_select "div.alert-success", flash[:success]
get :new
assert flash.empty?, "there shouldn't be any flash messages left after getting new page"
end
When I test this manually in a browser, it behaves as expected, but I'm not sure how to write the tests to reflect this. The first two assertions are passing (so the test recognizes that it is being redirected correctly, and that flash[:success] is being set to "Welcome!"). Based on that, I would think the assert_select statement would be able to find the div, but if fails with this error message:
Expected at least 1 element matching "div.alert-success", found 0.
So I tried adding puts #response.body right before the assert_select statement, which printed this:
<html><body>You are being redirected.</body></html>
This explains why it couldn't find the flash div, but I'm not sure how to "finish" the redirect so that it renders the correct view with the flash div. If I add a get statement right before the assert_select statement, the test passes, because now #response.body has the flash div. However, this doesn't seem like an effective test because it doesn't reflect the behavior of my application - the user doesn't have to make a new request to see the flash message.
It also doesn't guarantee that the flash message shows up on the correct page. If I use the statement get :new, the assertion still passes, which implies that the flash message would be present on the users#new view. Even if I use
get :show, id: assigns(:user).id
to make sure the flash message shows up on the correct page, this still feels wrong because 1) now it is a success response instead of a redirect response, and 2) it still doesn't explain why I have to manually request ANY page to find the flash div - according to the docs, "The flash is a special part of the session which is cleared with each request."
Anyway, is there a way to test that a flash message shows up correctly after a redirect?
I'm answering this question from the POV that what you're trying to test would be better tested elsewhere. If you're having a lot of trouble setting up the test, odds are there's a better way to test it :)
Testing that flash messages get cleared - flash messages are built into Rails, so it doesn't make a lot of sense to test something that the framework is handling for you.
Testing that elements show up on the page - view specs don't belong in controllers. Here is a description of the role of the controller:
A controller can thus be thought of as a middle man between models and
views. It makes the model data available to the view so it can display
that data to the user.
The controller is supposed to make data available to the view, not to specify how the data gets rendered. Imagine if you changed the name of your CSS class, then you'd have to also update your controller specs.
If you really want to test that flash messages show up, I would use an integration test.