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
Related
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
Messages are displayed green if sent by the current user, and blue otherwise. Following Rails convention, where does that logic belong?
Introdution
The user will visit /group/:id to see the list of messages, so the corresponding view is views/groups/show.html.erb and the corresponding controller is controllers/groups_controller.rb.
The message we want to display are in an array in #group, as #group.messages. The array is sorted by timestamp.
The code to style the color of the message is not important, but for simplicity purposes we will say there are two class selectors (one for from and one for to) and we can simply add a class attribute to the div that a message is within to change its color.
Both the user's sent and received messages are held in the array #group.messages.
If we have an individual message stored in message, we can test if it was sent by the current user with:
if session[:user_id] == message.user_id
Problem
Messages are ordered by timestamp and will need to be displayed in that order. For this reason, I can't see any clean way of handling the logic in the controller.
I would like to keep as much logic as possible out of the views and especially out of the partials, but after considering the options for rendering sent and received messages in different ways, the cleanest option I've found is to put the logic in the message partial.
Handling the logic in the message partial:
<% if message.user.id == session[:user_id] %>
<div class="to">
<p> <%= message.body %> </p>
</div>
<% else %>
<div class="from">
<p> <%= message.body %> </p>
</div>
<% end %>
Pros:
This method handles the logic with one if statement that is clean and simple
It allows us to make the code DRY because we won't have to use the logic anywhere else if we want it on other pages
Since every message only has a body, we don't have to make another partial to display messages without this formatting
Cons:
The logic is in the partial! I think people I'm working with or other programmers or even myself would first look in the controller then in the view then in the partial to make any changes or see the code
This doesn't feel like normal Rails convention
Handling the logic in the view:
Possibly two clean solutions -
1) Style the messages inside the logic or
2) Render a different partial for sent/received messages
Styling inside the logic:
<% #group.messages.each do |message| %>
<% if message.user.id == session[:user_id] %>
<div class="to">
<p> message.body </p>
</div>
<% else %>
<div class="from">
<p> message.body </p>
</div>
<% end %>
<% end %>
Rendering different partials:
<% #group.messages.each do |message| %>
<% if message.user.id == session[:user_id] %>
<%= render :partial => '/messages/sent_message', :message => message %>
<% else %>
<%= render :partial => '/messages/received_message', :message => message %>
<% end %>
<% end %>
Pros:
Either view solution keeps the logic out of the partial
It makes sense that showing something as one color or another is decided in the view
The view solution using two partials is clean and allows us to avoid styling within logic which also means that we can change the style within the partials and affect the look of messages everywhere.
Cons:
Both view options mean that our code is no longer DRY. Using these methods will mean that if we want the same functionality on 3 other pages, we will have to write the same code 3 more times
It makes sense that a view shouldn't be deciding anything
The view solution using two partials means that we will crowd the views/messages folder with partials, and still not have a default partial for rendering messages
Both of the view solutions just feel dirty in my opinion
My main points about my solutions -
No option allows for the logic to be held within the controller
Placing the logic inside the view means that to provide the same functionality on multiple pages, the same code will be written in more than one place
The option that looks the cleanest and makes the most sense to me means putting logic inside a partial, and there must be a better way.. right?
None of the solutions seem like they follow Rails convention
Which of the three options I coded best follow Rails convention?
Is it possible to place the logic in the controller?
Is there a better way to design this so that there is a clear solution following Rails convention?
What you probably have realized is that each of the three versions you described is either not DRY or not scalable. You've done a great job analyzing pros and cons of each option, so there is very little for me to add there. :)
To add presentation functionality to your models, Rails community uses Presenters. There is a great article on Presenters here that explains more about them.
Basically, you'll want to have one partial for message:
<div class=<%=#presenter.css_class%>>
<p> <%= message.body %> </p>
</div>
Then Presenter:
class MessagesPresenter
def initialize(message, current_user)
#message = message
#current_user = current_user
end
def css_class
message.user == current_user ? 'to' : 'from'
end
private
attr_reader :message, :current_user
end
And controller:
#presenter = MessagesPresenter.new(#message, current_user)
Voila! The presenter is available in both views and partials and is a great place to stash all presentation logic.
Since the only difference in these examples in the CSS class, you're repeating yourself quite a bit. Can't you add or remove a class on the tag depending on whether the tag belongs to the current_user or not?
This is really a presentation issue, and you can handle this simple logic for displaying the correct CSS tag using decorators (http://johnotander.com/rails/2014/03/07/decorators-on-rails/). I recommend using Draper (https://github.com/drapergem/draper).
First, for simplicity, add a current_user helper method to application_controller.rb to return the authenticated user.
Add a Decorator:
MessageDecorator.rb
def recipient_class
user_id == current_user.id ? "to" : "from" # (user_id delegates to message object)
end
Now your views can have much cleaner logic
Views
Message Partial:
<div class="<%= message.recipient_class %>">
<p><%= message.body %></p>
</div>
collection partial in the main view:
<%= render partial: "message", collection: #messages, as: :message %>
Finally, call decorate on messages in your controller action:
#messages = #group.messages.decorate
EDIT
You can also use a simple helper method rather than a decorator:
def css_class_for_message(message)
message.user_id == current_user.id ? "to" : "from"
end
I am trying to make a text box that displays a list of sorted users that sorts per every typed letter. From there the person can add the user as a collaborator. The result should be kind of like facebook's search feature when you are searching to add new friends. When I press a key I see a new 500 internal server error in the network section of the browsers console. Here is a snippet of the response:
<h1>
NoMethodError
in CollaborationsController#collaboration_search
</h1>
</header>
<div id="container">
<h2>undefined method `[]' for nil:NilClass</h2>
So I think the ajax is getting fired to the server but there is something wrong with the controller. Here is my code for the view, views/collaborations/_new.html.erb:
<%= form_for [wiki, collaboration] do |f|%>
<div class = "col-md-8">
<div class = "form-group">
<%= f.label :user_name %>
<%= f.text_field :user_name, class: 'form-control', placeholder: "Enter name" %>
</div>
<div class = "form-group">
<%= f.submit class: 'btn btn-success' %>
</div>
</div>
<%= #users_by_name.to_a %>
</div>
</div>
</div>
<%end%>
where the form above is a partial. The ajax is written in javascripts/collaborations.js:
$(document).ready(function()
{
// alert(1);
$('#collaboration_user_name').on('keyup', function() {
// text = $(this).val();
// alert(text);
$.ajax({ url: "/collaborations",
beforeSend: function( xhr ) {
xhr.overrideMimeType( "text/plain; charset=x-user-defined" );
}
}).done(function( data ) {
if( console && console.log ) {
console.log( "Sample of data:", data.slice( 0, 100 ) );
//alert()
}
});
});
});
Here is the collaboration#search_collaboration action within the collaborations controller:
def collaboration_search
name_search = params[:collaboration][:user_name].to_s
#users_by_name = User.where('name Like ?', "%#{params[:collaboration][:user_name]}%")
render json: #users_by_name.map do |user|
{ name: user.name, id: user.id}
end
end
And just for testing purposes I kept my collaborations/search_collaboration.js.erb very simple:
alert('hello');
however if someone can point me in the right direction for how to list the names of the users returned from the collaboration_search action that would be much appreciated!
The error seems to suggest that you're trying to index into something as a hash, when it is, in fact, nil:
undefined method `[]' for nil:NilClass
The [] refers to hash indexing -- such as params[:collaboration]. My immediate guess would be that you're not serving the params to this controller action in the format expected, so that params[:collaboration] is nil, and you're trying to index into that params[:collaboration][:user_name], provoking said error.
This jives with your current jQuery code, which doesn't send the data at all (where are you sending text as a parameter, either in the querystring or as a jQuery.ajax() param?), not to mention in that specific format.
You could either do something like this:
$.ajax({ url: "/collaborations?collaboration=#{text}",
#..etc
Or you could use the ajax function's data method to give it a parameter as a hash instead of just throwing it in the URL. The docs for that function should give you more information.
I'd strongly recommend the latter, as you expect nested hashes in your controller [:collaboration][:user_name], which is not easily supported in a querystring parameter.
To see what the parameters are coming in as, I suggest throwing something like this in your Ruby controller:
puts "PARAMS: #{params.inspect}"
That should go above anything causing an error. It will print out the params in your server log (the terminal tab where you typed rails server or rails s), so that you can see what the parameters are, and whether this hypothesis is accurate, and how to fix the problem.
As a final note, I don't think you're actually hitting your .js.erb file at all. Your action just returns a response to the jQuery ajax function. That response is in your function called on done(), and the response data is, in your current code, referred to as data. I'd alert that data once you've gotten past this error, to see how your controller is serializing things. And then, without that js.erb file, you can simply update the DOM with the results from the jQuery.
(Final note, I can't think of an occasion where you'd need to test for if console && console.log, and I'm not sure that test won't throw an error. I might be wrong, though.)
The UX Jargon for what you what you are trying to build is autocomplete dropdown or autocomplete combobox. It is actually a fairly complicated UI element that I wouldn't both programming from scratch. You should use a library like JQuery. JQuery is built into core Rails so I should just be a matter of including the right library. Here Is a sample of the element from JQuery.
http://jquery-ui.googlecode.com/svn/tags/1.8.7/demos/autocomplete/combobox.html
You can look at the source code using the browser development tools
Here is JQuery Autocomplete docs
http://jqueryui.com/autocomplete/
You can look at the code in you
Here is another implementation
http://www.jqwidgets.com/jquery-widgets-demo/demos/jqxcombobox/index.htm
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.
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] %>