Rails 3 / Controller / Flash hash - ruby-on-rails

I want to be able to pass multiple messages to the flash hash, inside of my controller, and have them display nicely together, e.g., in a bulleted list. The way I've devised to do this is to create a helper function in my Application Controller, which formats an array into a bulleted list, which I then pass to, in my case, flash[:success]. This is clearly not the Rails Way because, i.a., my bulleted list gets encoded. That is, instead of getting:
Message 1
Message 2
I get:
<ul><li>Message 1</li><li>Message 2</li></ul>
I'm sure I could figure out a way to raw() the output, but isn't there a simple way to get something like this working? Perhaps there's an option to pass to flash[]? Something else?

I used render_to_string and a partial instead of a helper to achieve something similar.
# app/controller/dogs_controller.rb
def create
#dog = Dog.new(params[:dog])
#messages=[]
if #dog.save
#messages << "one"
#messages << "two"
flash[:notice] = render_to_string( :partial => "bulleted_flash")
redirect_to(dogs_path)
else
render :action => 'new
end
end
Then I format the array of flash messages in an HTML list
# app/views/dogs/_bulleted_flash.html.erb
<ol>
<% #messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ol>
Which produces the following HTML
# http://0.0.0.0:3000/dogs
<body>
<div id="flash_notice">
<ul>
<li>one</li>
<li>two</li>
</ul>
</div>
...
</body>
If you need to continue using a helper then I think you need to append the html_safe method to your string to prevent it from being encoded (which rails 3 does by default). Here is a question showing how to use html_safe in a similar fashion

If you are using Rails3, try the raw method.
raw(my_html_string)
And it won't escape the html. Oh, sorry, I just read your last sentence. Check out this information, "Rails 3 flash message problems", it looks like it may be what you are looking for:
http://www.ruby-forum.com/topic/215108

Usually I would ask for more information about your views and layouts in this situation, because scaffolding doesn't display flash[:success] by default.
The way I solve this is to totally redo my flash messages usually, by making the flash[:whatever] an array every time, and in my layout handling that array instead of just the notice. Remember that flash is just a Hash, you're just setting values.
However if you just want to do this with the setup you have now (helper putting the HTML inside the flash[:success]), you can change the way that the flash messages are displayed in your layout file. By default they are just use <%= flash[:success] %>, which automatically escapes HTML. To make it not do that for the flash messages, change it to <%=raw flash[:success] %>

Related

What's the difference between the two ways of message rendering in Rails?

I copied an existing rails repo, but I found two ways of rendering the message to View files in different web pages.
This is the first one, which can render the "notice" or "alert" in the controller to the web page.
<% flash.each do |type, msg| %>
<div>
<%= msg %>
</div>
<% end %>
This is the second one, and I have no occasion to trigger it.
<% if #task.errors.present? %>
<ol>
<% #task.errors.each do |error, message| %>
<li>WARNING: <%= "#{error.capitalize} #{message}" %>
</li>
<% end %>
</ol>
<% end %>
I wonder what is the essential difference between them? Can they replace each other?
I wonder what is the essential difference between them?
These are two completely different concepts.
The first is iterating through the flash. This is basically just a simple cookie based store used to pass primatives such as strings, hashes or arrays to the next request. The flash is typically used when displaying a message to the user after redirecting.
For example:
def deny_access
flash[:alert] = "You must be signed in."
redirect_to new_session_path
end
You can also set flash messages for the current request by using flash.now. The flash is purged from old messages after every request. The flash is typcially used as a kind of system wide announcement system and displayed as a banner on the top of the page.
The second is iterating (badly) through the instance of ActiveModel::Errors attached to a specific instance of a model. This is a collection of validation errors that gets filled when .valid? is called (usually indirectly) on a model instance.
You use this when displaying the validation errors of a model in the current request cycle. Typically on the top of a form or inline by the corresponding input.
Since this data is just stored in memory in an instance variable it does not persist across redirects.
Can they replace each other?
No - not really. Both are used to provide user feedback but are very different tools to solve different jobs. Doesn't really stop some noobs from trying though.
See:
https://guides.rubyonrails.org/action_controller_overview.html#the-flash
https://guides.rubyonrails.org/active_record_validations.html

Rendering attribute with two different formats

I have an object that has an attribute using a markdown language for the past few years and recently the attribute is now switched to using html. How do I render all the past campaign descriptions with the markup language and how do I render all the new campaign descriptions with .html_safe
Html_safe
<h6 class="text-muted" itemprop="description">
<%= #campaign.product_description.html_safe %>
</h6>
Markdown
<h6 class="text-muted" itemprop="description">
<%= Campaign::Format #campaign.product_description, {render_html: true} %>
</h6>
How can I render both? That way For all the old campaigns it renders the markup and for the new campaigns it renders the html. When I include html_safe and the markdown on the same line it only does the markup and not the html from a WYSIWYG editor.
Several options:
Add a flag to your campaigns. Something like uses_html_description. Set its values correspondingly. And switch on that in the view.
Do a data migration and replace all descriptions in old format with the new html format.
Between these two, I'd go with the latter.
My first thought is that I would backfill your DB converting all the old records to the new format.
Otherwise, make a view helper method in app/helpers/application_helper.rb something like:
def description_text campaign
if campaign.created_at > Time.new(2019,3,1).in_time_zone
campaign.product_description.html_safe
else
Campaign::Format campaign.product_description, {render_html: true}
end
end
Note that you can probably also actually detect the HTML in the string if you'd prefer that to the time-based thing, like:
def description_text text
if text.starts_with? "<html" # or whatever
# ...

How to interpolate ruby inside of an interpolated bit of ruby in ERB

I'm trying to create a situation where one user makes message templates and another one can plug in values. I'm using the best_in_place gem, which will allow a user to edit the message on the show page.
The problem is this. When I call the message, with the required erb to make the gem work, it treats all of this as a regular string, not as ruby.
This is unclear, I'm sorry.
Here's the code.
#announcement.content = "The <%= best_in_place #announcement, :train %> is arriving in five minutes."
/show.html.erb
<%= #announcement.content %>
I want it to put "The click to set train is arriving in five minutes." and if the user clicks where it says "click to set train," a text field will open for them to edit (this is something the best-in-place gem does).
Instead, it puts "The <%= best_in_place #announcement, :train %> is arriving in five minutes."
I understand why it is doing this, but I don't know how to make it instead interpret the ruby I'm trying to pass in.
Ideas?
Use regular old string interpolation:
#announcement.content = "The #{best_in_place #announcement, :train} is arriving in five minutes."
You can use ERB to render any ERB template string. In this case something like:
<%= ERB.new(#announcement.content).result %>
Although you likely won't have access to all your Rails helpers, etc.
The Rails way to do this:
#announcement.content_type = :arriving
Later:
<%= render(partial: #announcement.content_type)
In _arriving.erb:
The <%= best_in_place #announcement, :train %> is arriving in five minutes.
TL;DR: ERB is not Ruby, and Rails uses both at different times.
You want simple Ruby string interpolation here:
#announcement.content = "The #{best_in_place #announcement, :train} is arriving in five minutes."
This is unclear, I'm sorry.
Not to worry, the Rails framework throws so many different new concepts at you it can be frustrating for newcomers.
Start from this: the Ruby framework builds the answer to the user's browser from a collection of resources Each file is evaluated by an interpreter for its own language. The trick is: look at the extension.
Files ending in .coffee will be compiled into javascript, files ending in .scss will become CSS, and in the same way files ending in .erb will yield HTML.
ERB is a language composed of mostly HTML already, plus a tag that allows you to interpolate Ruby. ERB stands for Embedded Ruby.
What about files ending in .rb, like the file in which you (surely) are evaluating #announcement.content = "The <%= best_in_place[...]" (a controller, I guess)?
Well, that's just pure Ruby :) that's why the ERB interpolation syntax <%= ... > is not recognized.
What you want to do in the controller, is (as you're trying to do) preparing the data for the view. The ruby in the <%= ... > tag in ERB will have access to the controller's instance variables, i.e. the variables with an # in front defined in the controller. But to define those, inside the controller, you should rely on Ruby alone.
Take-home message:
Be aware of which language you are writing in at each moment. For example:
# show.html.erb
<p>Here is ERB, which will be interpreted straight into HTML</p>
<% "Inside the '<% ...' tag is Ruby, but results won't show up in the HTML because there's no '<%='."%>
<% which_language = "Ruby" # Even variable assignments, and comments, do work %>
<%= "Inside the '<%=' tag, you're writing and interpolating #{which_language} :)" %>
I think the fact that I wasn't clear made it hard to answer this question.
What I'm doing is transforming user-inputted text (using a method in the model, called by the controller) to replace certain keywords with erb tags that call the best_in_place plugin. In my view, when presenting this content to another user, I wanted to call this content, which is saved as an attribute in the database, in such a way that it would render correctly for the other user to have the best_in_place functionality active.
Here's what I ended up doing. It is working, but if you have better ideas, please let me know.
In the announcements#create view, the user creates an announcement with certain pre-defined blocks of bracketed text as well as free-input text. For example, they might write "[train] is leaving from [platform] in [time] minutes."
When they hit save, the controller's create action calls the construct_message method from the model. It looks like this:
def construct_message(msg)
msg.gsub! '[train]', '<%= best_in_place #announcement, :train_id, :as => :select, collection: Train::list_trains, place_holder: "Click here to set train." %>' #note: list_trains and list_platforms are methods on the model, not really important...
msg.gsub! '[platform]', '<%= best_in_place #announcement, :platform_id, :as => select, collection: Platform::list_platforms, placeholder: "Click here to set platform." %>'
msg.gsub! '[time]', '<%= best_in_place #announcement, :number_of_minutes, placeholder: "Click here to set." %>'
end
Then, when I want to show that attribute in my view, I'm using render :inline, like this.
on announcements/:id
<p id="notice"><%= notice %></p>
<p>
<strong>Content:</strong>
<% announcement = #announcement %>
<%= render :inline => announcement.content, locals: { :announcement => announcement } %>
</p>
This allows the erb call that I wrote into the attribute to be functional.
Also note that I'm choosing to use a local rather than instance variable here; this is because in announcements#index, I also render this text and the table there uses local variables.

bootstrap_flash to display model error messages

I'm using Rails 4.0 and the twitter-bootstrap-rails gem which displays notices with bootstrap_flash above the yield statement.
One way I put errors in the errors collection is this:
if there_is_an_error
msg = "You have an error here..."
self.errors.add(:base, msg)
end
This should work because I display inline errors next to specific items, so I have no problem with that as far as I know. But I want to display validation errors at the top of the form as well sometimes.
What's the best way to display the validation error messages above the form, at the top, especially when using bootstrap?
I do it via flash, which acts like a hash - flash[:danger], flash[:success] etc:
In controller relevant to the form use flash[] before redirect_to (flash.now[] before render)
flash[:success] = "Signup passed successful"
and iterate over flash hash in application.html.erb (layout)
<div class="container">
...
<% flash.each do |key, value| %>
<%= content_tag(:div, value, class: "alert alert-#{key}") %>
<% end %>
...
<%= yield %>
...
</div>
Note that you better use standard bootstrap :key (:sucess, :danger etc) for proper automagic formatting
Alerts in Bootstrap 3
P.S. I personally use Bootstrap 3 gem bootstrap-sass by thomas-mcdonald, but approach should work for you too. Bootstrap 2 uses :error instead of :danger and .alert-block instead .alert
Hi there bootstrap_flash is designed to work with Rails' flash method. It can be configured to do what you want, including targeting a particular container to display in.
As far as validation goes it does not support it out of the box as many users were implementing their own solutions, typically client-side using ajax to get the errors from the server. We didn't get much consensus on what the right approach would be, so we left it out since other libraries are good at it.

Rails clear flash notice after JSON response

I am using Angular to send JSON request to the controller, in my controller I flash notice like this:
flash[:notice] = "Toon has been tagged"
After I want to use my rabl template to return JSON response and also include my flash notice content like this:
object #toon
attributes :id, :uuid, :get_tag_string
if flash
node(:flash) do |f|
flash.each do |k, v|
{ :msg => v, :name => k }
end
end
end
attributes :errors
My angular code handles the response and display the flash notice contents correctly. But here comes the problem, when the page refreshed the flash message is displayed again because of following code in my layout view:
<% flash.each do |key, value| %>
<div class="row-fluid">
<div class="span8 offset1"><%= content_tag(:div, value, class: "alert alert-#{key} center")%></div>
</div>
<% end %>
I could remove this or do an after_filter in my controller to call flash.clear
IS THERE A BETTER WAY TO DO THIS?
THANKS!!
I too am using angularjs with rails. The way I am handling error messages from the server (rails) is to use angular route changing events. Since you are dealing with flash messages from the server, it's really the same concept.
In my angular app where I display errors (in your case flash messages) I use an ng-show with a variable, e.g.,
<div ng-show="showMessages" class="alert">
{{myMessages}} // obviously here you may have an ng-repeat or similar
</div>
In my main angular controller I am setting $scope.showMessages (either true or false). At this point it's the same issue. In my controller I use the following callback to see when the route has changed. When it's changed I can set $scope.showMessages to false.
$scope.$on('$routeChangeStart', function (scope, next, current) {
$scope.showMessages = false;
}
I also use this routeChangeStart event to deal with times where I want the message to come out on the next page- by adding another var to control the "show this on the next page only".
I personally wouldn't go back to the server to clear the flash messages - that seems "expensive" whereas you wouldn't be forced to make that extra round trip.
Solve with flash.now in my controller :)
Thanks for your answer Arthur Frankel, liked your way to hand the messages

Resources