Control Structure in Rails Partial - ruby-on-rails

I'm building a Rails app up from the Sample App featured in Michael Hartl's book. In order to display error messages on user signup, I'm using a partial in the shared directory - app/views/shared/_error_messages.html.erb:
<%if #fact %>
<% #data = #fact %>
<% elsif #user %>
<% #data = #user %>
<% end %>
<% if #data.errors.any? %>
<div id="error_explanation">
<div class="alert alert-error">
The form contains <%= pluralize(#data.errors.count, "error") %>.
</div>
<ul>
<% #data.errors.full_messages.each do |msg| %>
<li>* <%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
Originally, this partial just started with something like:
<% if #user.errors.any? %>
However, since I've decided to re-use this partial to show errors on other pages, I'm having to use different objects (#user, #fact) depending on which page I'm using it on. This is easily solved by adding that IF statement at the top,
<%if #fact %>
<% #data = #fact %>
<% elsif #user %>
<% #data = #user %>
<% end %>
-but this feels icky. Is there a controller somewhere I should be putting this kind of logic for shared partials?

You can pass local variables to partial instead:
<% if object.errors.any? %>
<div id="error_explanation">
<div class="alert alert-error">
The form contains <%= pluralize(object.errors.count, "error") %>.
</div>
<ul>
<% object.errors.full_messages.each do |msg| %>
<li>* <%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
And in your template, for example:
<%= render 'shared/error_messages', object: #user %>

Marek's answer is probably the best. However, here is an alternative which though not scalable, is closer to what you have done.
Just replace:
<%if #fact %>
<% #data = #fact %>
<% elsif #user %>
<% #data = #user %>
<% end %>
with:
<% #data = #fact||#user %>

You could use render to point to the partial from your controllers for users and fact. Check out the API documentation on rendering partials.

Related

create instance variable in erb template

Say I have 2 erb views: a list view and a show view, I want to render the show view inside the list view
list view
<% #vehicles.each do |vehicle| %>
<h2><%= vehicle.type %></h2>
<ul>
<% vehicle.accessories.each do |accessory| %>
<li><% accessory.title %></li>
<% end %>
</ul>
<% end %>
show view
<h2><%= #vehicle.type %></h2>
<ul>
<% #vehicle.accessories.each do |accessory| %>
<li><% accessory.title %></li>
<% end %>
</ul>
the issue is the show view takes an #vehicle instance variable, how can I pass that down from the parent if I was going to nest these and it still shows up as an instance variable, accessed with #vehicle? something like:
<% #vehicles.each do |vehicle| %>
<%= render "show" %>
<% end %>
I guess you might want to make that show view a partial. Something like:
<h2><%= vehicle.type %></h2>
<ul>
<% vehicle.accessories.each do |accessory| %>
<li><% accessory.title %></li>
<% end %>
</ul>
Then you can do something like:
<% #vehicles.each do |vehicle| %>
<%= render "path/to/show_partial", locals: {vehicle: vehicle} %>
<% end %>
Naturally, you'll want to:
use the real path/to/show_partial, whever that happens to be, and
do some eager loading so that you're not doing a whole bunch of N+1 queries.

rails undefine method size nil class for

i tried to have this in my navaigation which will show products in cart
<% if !#order_item.nil? && #order_item.errors.any? %>
<div class="alert alert-danger">
<ul>
<% #order_item.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<% if #order_items.size == 0%>
<p class="text-center">
There are no items in your shopping cart. Please <%= link_to "go back", root_path %> and add some items to your cart.
</p>
<% else %>
<% #order_items.each do |order_item| %>
<%= render 'carts/cart_row', product: order_item.product, order_item: order_item, show_total: true %>
<% end %>
<% end %>
after adding this to the navigation when i visit other pages it fail but when the cart page it works. how can i show this in every page?
You can use before_filter to call in every controller. Where order_items should be written in application controller in a method.
I thing your #order_items is return the collection type , So please check your #order_items object have data ?
by using this methods
.empty? or .nil?
it return the true or false , you get you result.

How to display errors on a form_for with multiple resources

I have an order form where I can add order items
<h2>Order: <%= #order.title %></h2>
<h3>Items</h3>
<%= render #order.items %>
<h4>Add an item:</h4>
<%= form_for([#order, #order.items.build]) do |f| %>
<%= f.label :Item %>
<%= f.submit 'Submit' %>
<% end %>
I want to check if there are any errors when creating the item.
This is what I use on the order form:
<% if #order.errors.any? %>
<div id="error_explanation">
<h2>
<%= pluralize(#order.errors.count, "error") %> prohibited
this order from being saved:
</h2>
<ul>
<% #order.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
I don't know how to display errors when there is multiple resources in the form for.
I've run into this before and ended up wanting to really customize the messages. In order to do it, I used a solution like this:
<% if #errors.any? %>
<div id="error_explanation">
<h2>
<%= pluralize(#errors.count, "error") %> prohibited
this order from being saved:
</h2>
<ul>
<% #errors.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
This allows you to create an #errors object in your controller where you build array of messages. For example something like this:
#errors = []
#errors += #order.errors.map(&:full_messages)
#errors += #items.map(&:errors).map(&:full_messages).flatten
...
I hope this helps!

Show Rails errors, notices and devise messages in one place

I have my application.html.erb file where this code located:
<% if notice %>
<p class="notice"><%= notice %></p>
<% elsif alert %>
<p class="alert"><%= alert %></p>
<% end %>
<%= devise_error_messages! %>
All is great, but form validations are made by using (as usual), this:
<%= form_for #project do |f| %>
<% if #project.errors.any? %>
<h2><%= pluralize(#project.errors.count, "error") %> prevented this project from saving:</h2>
<ul>
<% #project.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
<% end %>
...
<% end %>
How to include them into main app page & show them always in top?
How to include them into main app page & show them always in top?
This is deeper than just adding a partial (as Umang Raghuvanshi suggested).
The problem is that the errors method is part of an #object, which means you have to make sure you're able to access this object whenever you want to output the errors in application.html.erb.
Partials should never be dependent on #instance_variables - they're meant to be loaded in any part of your app. You can pass local variables to them, allowing you to send predefined data as you need.
In your case, I would use a partial but populate it with a content_for:
--
This is what I'd do:
#app/views/layouts/application.html.erb
<%= render partial: "shared/errors" %>
#app/views/shared/_errors.html.erb
<%= content_tag :div, class: "errors" do %>
<% if errors.try?(:any?) %>
<% errors.full_messages.each do |msg| %>
<%= content_tag :li, msg %>
<% end %>
<% else %>
<% flash = %w(notice error alert) %>
<% flash.each do |f| %>
<%= content_tag :div, class: f, flash => [f.to_sym] if flash[f.to_sym] %>
<% end %>
<%= devise_error_messages! %>
<% end %>
<% end %>
#app/views/projects/new.html.erb
<%= form_for #project do |f| %>
<%= render "shared/errors", locals: {errors: #project.errors} %>
<% end %>
This will allow you to style the .errors div to be absolutely position at the top:
JSFiddle
#app/assets/stylesheets/application.css
.errors {
display: inline-block;
width: 50%;
position: absolute;
top: 5%;
}
Extract the code into a partial and include it into your application.html.erb.
_your_partial_name.erb
<% if #project.errors.any? %>
<h2><%= pluralize(#project.errors.count, "error") %> prevented this project from saving:</h2>
<ul>
<% #project.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
<% end %>
application.html.erb
<%= render partial: 'your-partial-name' %>
<% if notice %>
<p class="notice"><%= notice %></p>
<% elsif alert %>
<p class="alert"><%= alert %></p>
<% end %>
<%= devise_error_messages! %>
You could also try using flash.

Ruby On Rails - Reusing the error message partial view

Problem
I was trying to reuse the Error Message block in my Views.
Below was the block written in positions/_error_messages.html.erb
<% if #position.errors.any? %>
<div id="error_explanation">
<div class="alert alert-error">
The form contains <%= pluralize(#position.errors.count, "error") %>.
</div>
<ul>
<% #position.errors.full_messages.each do |msg| %>
<li>* <%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
The problem was I have to created similar partial view in every Model which is kind of repeating the same code with different object i.e. #user, #client etc.
Remedy
I have created one erb in shared folder shared/_error_messages.html.erb and wrote the below code.
<% def error_message(active_object) %>
<% if active_object.errors.any? %>
<div id="error_explanation">
<div class="alert alert-error">
The form contains <%= pluralize(active_object.errors.count, "error") %>.
</div>
<ul>
<% active_object.errors.full_messages.each do |msg| %>
<li>* <%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<% end %>
and then in view file. positions/new.html.erb I wrote the below code
<div id="errorbox">
<%= render "shared/error_messages" %>
<%= error_message(#position) %>
</div>
It means now I can use the same code in all my Create and Update operations.
I want to know, Is that a correct way to do it? Or is there any other option?
No, defining methods in views is not correct way to do it. I think you should rather substitute #position from your first partial with local variable named in more generic way, for example object and render this partial with:
<%= render 'shared/error_messages', object: #position %>
which passes #position as local variable object to the partial.
<%= render partial: 'shared/error_messages', locals: {position: #position} %>
Now in your partial _error_messages.html.erb in the shared folder you can use the position variable.
Refer to http://api.rubyonrails.org/classes/ActionView/PartialRenderer.html for more help.
It's not the best way to do it.
replace #position with something more generic like 'obj' (as an example) in your first block of code. So it will look like this.
<% if obj.errors.any? %>
<div id="error_explanation">
<div class="alert alert-error">
The form contains <%= pluralize(obj.errors.count, "error") %>.
</div>
<ul>
<% obj.errors.full_messages.each do |msg| %>
<li>* <%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
All you did there was replace #position with obj.
Now take the above code, and put it inside your shared folder as '_error_messages.html.erb'
Now for every file where you need the error messages you can render the partial and replace obj with whatever instance variable is used in that file. (at this point you would replace any error message code in your files with the code below, depending on which instance variable you are using. examples below)
in positions:
<%= render 'shared/error_messages', obj: #position %>
in user:
<%= render 'shared/error_messages', obj: #user %>
in client:
<%= render 'shared/error_messages', obj: #client %>
etc...
the obj: #client #or any instance variable
switches out the 'obj' of the partial and puts in the instance variable in it's place. Hope that helps!

Resources