Using HTML in Rails flash messages - ruby-on-rails

I use flash[:error] to display a simple message to users that they cannot do a delete operation under certain conditions. I also have a link that will help them to get information about the entity that they wanted to delete and why they cannot do so.
Is it advisable to include this hyperlink in the flash message? Which would mean that I would have a HTML fragment in my controller. If not, how would I go about doing this?

If you want to include a link in your flash message from the controller there are 2 issues. Generating the link and then getting it displayed as HTML.
To use the link_to helper in the controller, fully qualify it.
To have the string display as html (instead of being escaped), call the html_safe method on the string. So the line in your controller might look like:
flash[:error] = "You can't do that. #{ActionController::Base.helpers.link_to "Here's why.", '/more_info.html'}".html_safe

the flash object is a holder for storing view fragments/messages and persist them for one redirection using the session.
I see absolutely no problem in storing a link, or better an URL.
example :
redirect_to posts_path, :alert => "You cannot do that", :flash => { :url => post_path(#post) }
and in layout view, the usual suspects :
- if flash[:alert]
...
- if flash[:url]
= link_to "blah blah", flash[:url]

You can. You can add some Helper in your controller too.
Or you can do it by i18n system.

Related

Flash message with html_safe from the controller in Rails 4 (safe version)

In my controller I have the following code:
format.html { redirect_to new_customer_url,
notice: %Q[ A customer already exists with with this shopping id. Edit this customer #{view_context.link_to("here", edit_customer_url(#duplicate))}.
].html_safe
I would like to be able to include a link in a flash message, so (as you can see) I call html_safe to unescape the string. However, it seems that as of Rails 4.1 this is now handled differently. (See here and here)
A solution to this has been provided in this question. However, it only does so by moving the html_safe call to the view, having the effect of unescaping all flash messages.
I would prefer to be a bit more paranoid than that, is there any way to include the link in the flash message from the controller?
Here is one possible way to solve this problem. Add a before filter to your ApplicationController which will make flash[:notice] html safe only if flash[:html_safe] is set. Then you can control when and when not to make notices html safe completely from the controller.
before_filter -> { flash.now[:notice] = flash[:notice].html_safe if flash[:html_safe] && flash[:notice] }
Then your example could be modified to this:
format.html do
redirect_to(
new_customer_url,
notice: %Q[ A customer already exists with with this shopping id. Edit this customer #{view_context.link_to("here", edit_customer_url(#duplicate))}.],
flash: { html_safe: true }
)
end

Rails - how to pass created record from the new form to a redirected page

I think this is a pretty simple question but nothing I've read has answered my question directly:
I have a new products page with a standard form. After successfully submitting the form, I redirect to a custom controller action and view called "thanks".
On the "thanks" page, I want to be able to print the name of the product just created and possibly some other attributes.
How do I pass the object just created into my new action? Right now the controller looks like this:
def create
#product = Product.new(params[:product])
if #product.save
flash[:notice] = "Successfully created Product."
redirect_to thanks_path
else
render :action => 'new'
end
end
def thanks
end
You can't send object through redirect.
There are three ways to solve your problem:
Render the 'thanks' template directly(not action #thanks)
render 'thanks' # thanks template
You can send whatever instance variable to this template directly. #thanks is no longer needed in this case.
Drawback: The url won't be changed.
Convey messages through session
If you want to show certain messages, you can prepare it in #create and send it through session or flash(part of session actually). flash is better as you don't need to clear it manually.
Note: You may want to use ActiveRecord as session storage if the message size is big, otherwise you'll meet CookiesOverflow by default setting.
Send very simple message through session say obj_id
Similar to #2 but I thinks this is better than #2. In #thanks, you can construct complex message according to if obj_id is present, what is the id and then find related data through db.
You have two fairly decent options.
First, you could adjust the thanks_path route to take an id parameter, and call it like redirect_to thanks_path(#product). Then you can call it up in your thank you method like any standard show method. It might be worth mentioning that if you are going to be displaying sensitive information on the thank you screen, you may want to use a random uuid, instead of an id, to look up the product.
A better way might be to not redirect at all, but rather adjust your view from simply drawing the form to something like this:
<% if #product && !#product.new_record %>
THANK YOU MESSAGE GOES HERE
<% else %>
EXISTING FORM GOES HERE
<% end %>

Redirecting to a random page while maintaining browser history

I'm trying to implement a "random page" link, which will render to the user a random article from the database. I've tried two separate (but similar) approaches, in both of which I route the URL "/random" to the "random" method in the ArticleController. Here's the first:
def random
offset = rand(Article.published.size)
#article = Article.published.offset(offset).first
render :action => 'show'
end
This works for serving random articles, but there are two issues: First, the URL doesn't update to the correct article, so users can't copy the link or bookmark the article; second, the previously viewed random articles don't show up in the browser's back button's history (i.e. pressing "Back" brings the user back to the page they were on before clicking "random" for the first time).
The second approach substituted render with redirect_to:
def random
offset = rand(Article.published.size)
#article = Article.published.offset(offset).first
redirect_to #article
end
This fixes the first issue - it's a redirect, so the browser is actually redirected to the appropriate URL for the randomly selected article (so it's available for copying/bookmarking). However, the problem with the Back button still remains. Moreover, it feels a bit wrong to co-opt an HTTP Redirect for something like this.
What would be the best way to go about serving random articles, while displaying the correct URL for the article and also maintaining a browser history chain?
Why don't you make actually a LINK for random article?
Helper:
def random_article_link
random_article = Article.find_by_sql("SELECT 1 FROM articles ORDER BY RANDOM() LIMIT 1") # for MySql RAND()
link_to "Random Article", random_article
end
In your approach you can not change URL string on a fly on a controller level. Only on routing level using Constraints.
Umm what about
#article = Article.find(rand(Article.count))
redirect_to #article
I wanted to do something similar and was also having trouble. Here is the solution I came up with:
In your controller you don't need a new page articles/random. I'm assuming you want the link on your article page which would be the show action in your controller.
def show
#article = Article.find(params[:id]
#random_article = Article.order('random()').first
end
Then in your view show.html.erb file
<%= link_to "Random Article", #random_article %>
Expanding on Chris's comment a bit - it's possible create a link within the view which will generate a random id for the next link (if you have some reason to do this...that's up to you).
<%= link_to 'Show me a random thing', thing_path(rand(1..4)) %>
You could drop this wherever and when clicked it'll simply route to a random page. Of course you'll want to change the number to reflect actual :id 's

How to access validation messages in partials?

So, each rails project i seem to run into this problem and would be really grateful if someone could enlighten me:
With a "normal" setup when the form sits on the view immediately associated with the url/controller it is pretty straightforward, a render => :action on fail will display the validation message.
Now what i have is a form in a partial sitting on a page whose url/controller is a show/:id and not the create action of the form, the validation kicks in but i cannot get the validation message to display because i can't trigger the correct render action...
CLosest i got is a render => #object but there is no css/layout, i can pass a message through a redirect with flash[] but it feels wrong, same with jquery/client error messages...
So how can i "cleanly" display validation messages of a form in a partial (under another controller/action than the parent page)?
(thanks in advance for your help)
edit: can't paste the actual thing now but i'll do my best to explain
i have a main page e.g. article/show/01, on this page is the content of the article (#article) and then at the bottom of the page is a partial _commentform with a form to post a comment. This form is bound to a Create action of a different controller (comments controller).
Now if the form were on its own "page"/url instead of a partial, say /comment/create, i would jus do:
if #comment.save
redirect_to #comment
else
render => :create
end
and the validation would display normally.
In my case the form is in a a partial on the article/show/01 url, what should be the equivalent to the code above so that on validation fail error messages are displayed on the parent url, like "render article/show/01" ?
I am sure it is easy but i cannot get it to work (i just can redirect on success but cannot display the errors with a render)
I don't think the best way to display validations errors is to render a partial.
IMHO, the best and clean way to display errors messages using the styles/css you or your webdesigner wants is by implementing your own error_messages method in a FormBuilder.
For example, here is the error_messages method I've implemented for my latest project.
Here is an example that will output the errors list in ul/li's with some custom styles...
Just customize this and put your form builder in app/helpers...
class StandardBuilder < ActionView::Helpers::FormBuilder
def error_messages
return unless object.respond_to?(:errors) && object.errors.any?
errors_list = ""
errors_list << #template.content_tag(:span, "There are errors!", :class => "title-error")
errors_list << object.errors.full_messages.map { |message| #template.content_tag(:li, message) }.join("\n")
#template.content_tag(:ul, errors_list.html_safe, :class => "error-recap round-border")
end
end
Then in my forms :
= form_for #post, :builder => StandardBuilder do |f|
= f.error_messages
...
No need to display/render another partial. And that's all :).
If you want to display anything (including error messages) in a partial you have two ways
1 - Define it in the controller action where the partial is called
2 - pass the message as a parameter to the partial
1 - Example
in your controller/action
if #comment.save
redirect_to #comment
else
#messages = "This is a message"
render => :create
end
in your partial
you can access the #message variable
2 - passing the variable to the partial
render :partial => “<partial name>”, :locals => { :param1 => “value”}
<partial name> – name of your partial (Ex /partials/footer)
:params1 – parameter name
“value” – value
hope this helps
thanks
sameera

What approach do you take for embedding links in flash messages?

The ability to have flash messages (notice, error, warning, etc) with embedded links is nice from a user interaction standpoint. However, embedding an anchor tag inside a flash message from the controller is dirty.
Let's assume that a flash message like this is good for usability*:
Example Flash Message Notice with an Embedded Link http://img.skitch.com/20090826-xbsa4tb3sjq4fig9nmatakthx3.png
(borrowed from DailyMile.com)
What tactic would you take between the controller and view to employ something like this cleanly?
Just thought I would share this, since found answer I was looking for elsewhere:
Works on rails 3.1
flash[:notice] = "Proceed to #{view_context.link_to('login page', login_path)}".html_safe
Glenn Gillen has an approach that he calls Useful Flash Messages in Rails.
I modified his code snippets to be a little more idiomatic (for me at least).
The controller fills the flash like this:
flash[:notice] = "Your profile was updated. %s"
flash[:notice_item] = ["Edit again?", edit_profile_path(#profile)]
You then might have helpers that look something like this:
def render_flash_messages(*keys)
messages = keys.collect do |key|
content_tag(:p, flash_message_with_item(key), :class => "flash #{key}") if flash[key]
end.join
content_tag(:div, messages, :id => "flash_messages") unless messages.blank?
end
def flash_message_with_item(key)
item = flash["#{key}_item".to_sym]
substitution = item.is_a?(Array) ? link_to(*item) : item
flash[key] % substitution
end
The view looks simply like this:
<%= render_flash_messages(:error, :notice, :warning) %>
The view (via the flash_message_with_item helper) is responsible for creating the anchor tag, but the controller manages what goes into the flash message including an optional resource for further action.
You could create a helper method to render out partials based on the value passed back in the flash message.

Resources