I am using the following code in my layout to display two types of flash messages:
<% if !flash[:notice].nil? %>
<div class="row">
<div class="flash notice col-xs-12">
<%= flash[:notice] %>
</div>
</div>
<% end %>
<% if !flash[:error].nil? %>
<div class="row">
<div class="flash error col-xs-12">
<%= flash[:error] %>
</div>
</div>
<% end %>
<%= debug(flash[:notice]) %>
<%= debug(flash[:error]) %>
They both work fine, but whenever one is triggered, it will still appear for one additional page view. I'm not using any caching gems.
Why is this happening? And how do I fix it?
Use flash.now instead of flash.
The flash variable is intended to be used before a redirect, and it persists on the resulting page for one request. This means that if we do not redirect, and instead simply render a page, the flash message will persist for two requests: it appears on the rendered page but is still waiting for a redirect (i.e., a second request), and thus the message will appear again if you click a link.
To avoid this weird behavior, when rendering rather than redirecting we use flash.now instead of flash.
The flash.now object is used for displaying flash messages on a rendered page. As per my assumption, if you ever find a random flash message where you do not expect it, you can resolve it by replacing flash with flash.now.
Related
the problem I have is probably easy to solve, although I did a lot of search and cant find a solution.
I have in my _errors.html.erb
<% if obj.errors.any? %>
<div class="row">
<div class="col-md-8 col-md-offset-2 col-xs-12">
<div class="panel panel-danger">
<div class="panel-heading">
<h2 class="panel-title">
<%= pluralize(obj.errors.count, "error") %>
prohibided this form from being saved:
</h2>
<div class="panel-body">
<ul>
<% obj.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
</div>
</div>
</div>
</div>
<% end %>
Then I have in my edit.html.erb that has a form and:
<%= render 'layouts/errors', obj: #my_obj_here %>
Then in the controller update/create (lets use the update as example):
def update
if #my_obj_here.update(params[:my_obj_here].permit(:body))
redirect_to my_path_here_path(#my_obj_here), notice: "Something."
else
render 'edit'
end
end
The issue happens when I try to update and the submit info is invalid, which will fall into the "render 'edit'"
The errors show correctly (in this case max length 100) but my url changes from:
my_obj_here/1/edit to my_obj_here/1
which should not happen.
So next I tried replacing the "render 'edit'" with "redirect_to :back" but this just ignores the
<%= render 'layouts/errors', obj: #my_obj_here %> in the edit.html.erb.
Can someone help me out figuring out how to render the same my_obj_here/1/edit?
I believe I need to use the "render" my method because the redirect will just skip the errors partial.
Also, in my update method you probably noticed this "if true":
redirect_to my_path_here_path(#my_obj_here), notice: "Something."
I could the the same and just change my code to:
redirect_to :back, :notice => "something."
This would work, but does not show the errors as I want them to shown when my the errors partial is used.
The errors show correctly (in this case max length 100) but my url
changes from: my_obj_here/1/edit to my_obj_here/1 which should not
happen.
This is is a very common misconception. Rails uses RESTful conventions where the HTTP methods used are extremely important.
When you click an edit link it you're performing a GET request to my_obj_here/1/edit. This is an idempotent action - the result is always the same and it does not alter any resource. In fact the new and edit actions in Rails don't do anything but display forms.
When you submit the form your sending a PATCH request to my_obj_here/1. This is a non-idempotent request as it alters the resource. When the validation fails and you render you're actually displaying the result of an attempt to update a resource. This is fundamentally different than the GET request to my_obj_here/1/edit - it's not cacheable and cannot be revisited.
http://guides.rubyonrails.org/routing.html#resource-routing-the-rails-default
I am using Devise to handle user authentication. When a user signs up, I am seeing 2 flash messages displaying the same text.
The blue well I believe is rendered through my application.html.erb:
<!-- Notice -->
<% if notice %>
<div class="alert alert-primary" role="alert">
<center><%= notice %></center>
</div>
<% end %>
However, I am unable to figure out where the 2nd message comes from. Any advice on where I should ook?
you may be using simple_form_for. That gem takes care of the error notice.
I have a Rails 3.2 app that manages students. It has a fairly typical nav bar across the top (Foundation 5) which contains a quick search field. The nav bar is displayed on every page of the site.
If you enter a valid (numeric) student ID into the search field, you simply jump to that student's page. If you enter text or other non-numeric input, you get a flash error asking for valid input. If you enter an id that's not found, you get a flash notice saying it wasn't found. In either of the latter two cases, the controller should just drop you back to whatever page you came from and display the appropriate flash message.
For starters, here's the search field in the view:
<%= form_tag search_students_path, method: 'get' do %>
<div id="nav-search" class="row collapse">
<div id="nav-search-field" class="small-21 columns">
<%= text_field_tag :search, nil, autocomplete: 'off' %>
</div>
<div id="nav-search-icon" class="small-3 columns">
<%= submit_tag ''.html_safe, class: 'button fa fa-search spin', name: 'submit' %>
</div>
</div>
<% end %>
And here's the controller action:
def search
session[:return_to] ||= request.referer
if params[:search].to_i.zero?
flash[:error] = %Q[<i class="fa fa-times fa-fw"></i> Please enter a numeric student ID.].html_safe
redirect_to session.delete(:return_to)
else
id = params[:search].to_i.abs
#student = Student.search(id).first
if #student
redirect_to #student
else
flash[:caution] = %Q[<i class="fa fa-warning fa-fw"></i> Sorry, we couldn't find a student with ID #{id}.].html_safe
redirect_to session.delete(:return_to)
end
end
end
Lastly, here's the code for rendering flash messages in application.html.erb:
<% flash.each do |key, value| %>
<div data-alert class="alert-box cbc-<%= key %>">
<%= value %>
×
</div>
<% end %>
In Chrome and FireFox this works exactly as expected. The flash appears for one request, then disappears. However, in Safari, once the flash comes up it never goes away for that page. So if you get the error flash on the home page, for example, you can refresh all you want. It stays put. You can go to another page, and then come back, and it's still there. The same is true for other pages. Once the flash message has appeared on a given page, it doesn't go away.
Thus my question: how can I get Safari to clear the flash after the first request?
I'm aware of the whole "flash vs. flash.now" issue when rendering pages. But even then, the flash will disappear if you simply refresh. I actually tried flash.now in this case, but then the flash isn't displayed at all in any browser.
Since this appears to be a browser-specific problem, here are some further stats on my system:
Mac OS X 10.9
Safari 7.0
Rails 3.2.16
One final observation. After playing around with this issue in Safari, I noticed that if I clicked my bookmark for http://localhost:3000/, that would clear the flash. Of course, all the navigation links in my site layout contain relative paths, whereas the bookmark is calling a full url.
Anyway, hope that made sense. Thanks in advance for your help!
i was wondering if it was possible to only affect one tab when clicking through pagination? im using the will_paginate gem
i have something that looks like...
<div class="tab-content">
<div class="tab-pane active span8" id="received">
<h3>Received Messages</h3>
<%= render 'received_message_feed'%>
</div>
<div class="tab-pane span8" id="sent">
<h3>Sent Messages</h3>
<%= render 'sent_message_feed'%>
</div>
</div>
which renders...
<% if #sent_message_items.any? %>
<ol class="microposts">
<%= render partial: 'shared/message_item', collection: #sent_message_items %>
</ol>
<%= will_paginate #sent_message_items %>
<% end %>
i have an equivalent received_message partial as well.
in my first block of code, i have 2 tabs, one for my received messages and one for my sent messages. lets say i click on my sent tab and then click on page 2. two problems arise from this.
the page refreshes and goes to my received messages tab, which is at first active tab. is there a way to make it go back to sent?
the bigger problem, when i click on page 2 while in sent tab, it will also load page 2 on my received tab. is there a way to prevent this?
im not sure how to fix this... help would be appreciated. thank you!
UPDATE.
i winded up using ajax pagination, so then the page doesn't ever reload and problem 1 is circumvented. however problem 2 is still an issue...
i fixed it.
i winded up using ajax pagination, so then the page doesn't ever reload and problem 1 is circumvented.
and as for problem 2, this saved the day
http://blog.devinterface.com/2011/08/tips-multiple-pagination-with-will_paginate/
I am using temp data as follow in my controllers - very simple, when there is a problem:
TempData("StatusMessage") = "You have no items set to Auto-Ship."
Then on every page I have a user control as follows:
<div class="error-container">
<% If TempData.ContainsKey("ErrorMessage") Then%>
<script> $('div.error-container').show();</script>
<div class="msg-error"><p><%=TempData("ErrorMessage") %></p></div>
<% End If%>
<% If TempData.ContainsKey("StatusMessage") Then%>
<script> $('div.error-container').show();</script>
<div class="msg-status"><p><%=TempData("StatusMessage")%></p></div>
<% End If%>
<ul></ul>
</div>
Problem is when I do have an error added to tempdata it shows up properly on the first request but ALSO shows up again on the next request as well - which is obviously very confusing and not a desired behavior.
I am not using any IoC, I did see the post with the same problems when using that.
The sole purpose of TempData is to persist until the next request. Stuff you do not want to persist until the next request should go into ViewData, instead.
Realistically, this means that TempData is only safe to use when redirecting. When not redirecting, the "next request" could be anything.
would this be acceptable (removing the error once it has been shown):
<% If TempData.ContainsKey("ErrorMessage") Then %>
<script> $('div.error-container').show();</script>
<div class="msg-error"><p><%=TempData("ErrorMessage") %></p></div>
<%
TempData.Remove("ErrorMessage")
End If
%>