Variable scope in Rails 4 application - ruby-on-rails

I'm learning rails and I have a test app in which I have listings and locations. There's a many-to-many relationship between them. My index page lists the listings, and provides (in construction) filtering functionality that includes checking locations. This is (partially) done with this, in the index.html.erb file:
<ul class = "list-inline">
<% #locations.each do |location| %>
<li> <label class="checkbox"> <input type="checkbox" value="<%= location.id %>" id="inlineCheckbox1"> <%= location.name %> </label></li>
<% end %>
</ul>
It does the job of displaying all locations with a checkbox to the left of each name.
Now, when adding or editing a listing, I need to let the user select which locations, apply to the listing, so I follow the same idea, and in the _form.html.erb file I have the following code as part of the form:
<select class="multiselect" multiple="multiple">
<% #locations.each do |location| %>
<option value="<%=location.id%>"><%=location.name%></option>
<% end %>
</select>
But this time, I get the following error, whenever going to the new or edit pages:
undefined method 'each' for nil:NilClass
with the
<% #locations.each do |location| %>
line being highlighted.
What am I missing?
(I have omitted code that I thought is irrelevant for the issue in order to now overwhelm you, but I may be wrong).
SOLUTION
Thanks for jumping in with your answers. Sorry I couldn't reply before, as I was at work and without access to my code.
I feel very sheepish... I was missing what is now obvious. The code for my New and Edit actions in the controller needed to have:
#locations = Location.all
Most of you said that in one way or another. I had to choose one answer and I did the one that appeared to me as the most straightforward, being also in comments on the question itself. Thanks!

Please double check your action to make sure you have the # variable in your action. I imagine it should be like this
#listing = Listing.find(params[:id])
#locations = #listing.locations

Your #locations is set in your index action, but it does't live forever. Just long enough for the index page to render. You can either set it again in your edit action, or just change your code to this:
<% Location.all.each do |location| %>

You might not be setting your #locations in your new action.
You can set it like this:
#locations = Location.all
In this case, however, I would like to recommend using a collection_select:
<%= f.collection_select(:location, Location.all, :id, :name, {multiple: true})%>

Local Vars
The problem, without seeing your controller or model, will be with your use of a partial
Rails partials don't use the same #instance variables as their parent views - you have to pass local vars to them (probably because they're designed to be used in many parts of an app; consequently passing local vars keeps them consistent):
#Parent
<%= render partial: "form", locals: { locations = #locations } %>
#_form.html.erb
<% locations.each do |location| %>

Related

Rails partial for collection, only display certain things for first element

I have a collection of elements I'm rendering in a partial, but I only want to display a certain element with the very first element. My specific instance is displaying email addresses but I only want the email icon to show once next to the first instance (similar to how the Android Contacts app does).
I have a very "hacky" solution that uses instance variables in the view, which is not a good practice. But I'm struggling to find a cleaner way to implement what I want.
The controller:
#email_addresses = EmailAddress.order(:primary) # primary is a boolean value
The partial:
# views/email_addresses/_email_address.html.erb
<div class="email-address">
<% unless #email_icon_displayed
<% #email_icon_displayed = true %>
<div class="email-address-icon">
<span class="icon email"></span>
</div>
<% end %>
<div class="email-address-value">
<%= email_address.value %>
</div>
</div>
Calling partial in view:
<%= render partial: "email_addresses/email_address", collection: #email_addresses %>
This works properly and only displays the email icon for the first element, but instance variables in the view seems like a bad idea.
This may be a little late, but I had the same goal and I achieved it by using a "hidden" counter variable in Rails partial collections.
I called my partial collection like this:
<%= render partial: "questions/possible_answer",
collection: question.possible_answers, as: 'value' %>
And inside my partial I can access the variable value_counter, which increments for each partial. So to run something with the first element only, I did it like this:
if value_counter == 0
#do something
end
I think your case you would need to access it like this: email_address_counter
Found this solution here: https://coderwall.com/p/t0no0g/render-partial-with-collection-has-hidden-index

How to show items which belong to the category

I'd like to answer what I'm doing wrong. So, I'm trying to list all products that belong to the category, on the category's page. Here is the code:
<% #product = Product.all%>
<% #product.where("category_id = ?", params[:#category_id]).each do |product| %>
<%= product.title %>
<%end%>
But there is nothing showing up on my page. So, what's wrong?
There is a whole bunch of problems with your code.
1) Read guides for starters.
2) You have to define an instance variable in controller's action, and then in view just use this variable in your loop. I assume, it is index action you have view for. If so,
def index
# this variable will be used in view
#products = Product.where(category_id: params[:id])
end
and then in view
#products.where(category_id: params[:category_id]).each..
Also, Make sure you have in params what you expect (inspect the params if not sure).
3) You do not execute code, so nothing is being output.
In erb to make things being evaluated you either use - or =. You used none of these. Here is how it should look like:
# notice dash at the beginning of the line
<%- #products.each do |product| %>
<%= product.category_id %>
<% end %>
probably you want read :category_id from params, not :#category_id (so it should be params[:category_id], not params[:#category_id]).

Cleaning up view ruby logic and separating concerns into model/controller

I want to display a random assortment of 6 tools from my database on my home page. I have created a Pages controller with a home action.
This is my Pages controller:
class PagesController < ApplicationController
def home
#tools = Tool.all
end
end
Then in my home.html.erb view I use the .sample method to grab random tools from my database as such(I repeat this 6 times using tool1, tool2, tool3, etc variables for each):
<% tool1 = #tools.sample %>
<%= image_tag tool1.tool_image.url(:medium) %>
<%= tool1.name %>
<%= tool1.description %>
I am wondering if there is a better way to do this. It seems that I have logic in my view and there must be a way to move that logic somewhere else? My model, controller, etc. How would one go about cleaning this code up so that it's good rails code? Or maybe this is good rails code and I just don't know it since I am a beginner.
Your controller doesn't need to extract everything from the tools_table, so I'd first remove the .all. Your example makes it seem like you just need 6 random objects from the database, here's one way to do that:
class PagesController < ApplicationController
def home
#tools = Tool.order("RANDOM()").first(6)
end
end
Then in your view you can just loop through those:
<% #tools.each do |tool| %>
<%= image_tag tool.tool_image.url(:medium) %>
<%= tool.name %>
<%= tool.description %>
<% end %>
In addition to Anthony's answer.
To clear up the view with some rails magic you can also add a partial to your app/views/tools called:
_tool.html.erb
Looking like:
<%= image_tag tool.tool_image.url(:medium) %>
<%= tool.name %>
<%= tool.description %>
And then change your view to
<%= render #tools %>
And Rails will know what to do if #tools is a collection of tools 😄

Moving rails from to from _form.html.erb to application.html.erb

I have most of the functionality done for a site. Now I am trying to make it look nice. I have a _form.html.erb that works great.
<%= form_for(#card) do |f| %>
<% if #card.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#card.errors.count, "error") %> prohibited this card from being saved:</h2>
<ul>
<% #card.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="field">
<%= f.label :event %><br />
<%= f.text_field :event %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
Files
view
- cards
-- new.html.erb
-- index.html.erb
-- show.html.erb
- layouts
-- application.html.erb
- pages
-- index.html.erb
I make a call for the form from new.html.erb and it works sends it to show.html.erb, just as I want. I'm using bootstrap and decided to make use of the nav bar. I have placed the nav bar code into the application.html.erb. It works just fine, well kind of. I want what would normally be a search function to be the add a new card.
When I add the form call it does not work, when I add it directly to the application page it does not work. I'm not sure, I have spent hours on this. I got it to work only on the show.html.erb page, both index pages would error out. I honestly don't remember how I did this though.
I'm trying to learn by doing, but I am stuck and need some help.
Thank you,
Ian
I guess that when you say that its working in your new.html.erb you have a new action inside your cards_controller, and inside this action you have something like: #card = Card.new
Well, if you want to put this form in another view, like in the application.html.erb you need to set first your #card variable, so you can do something like:
# application_controller:
before_filter :new_card
def new_card
#card = Card.new
end
be aware that all the controller that inherits from application controller will set this #card variable
#instance_variable
The underlying problem here is that you're calling a partial - by design, these are meant to give you the ability to call the functionality the file contains anywhere in your application
The problem you have is you're referencing an #instance_variable directly in your partial.
This isn't normally an issue - if you're using partials like you were originally (to modularize views), it should be okay. The problems arise when you try and use the partials in a more generalized way, as you are doing now:
#app/views/controller/_form.html.erb
<%= form_for(#card) do |f| %>
This relies on the #card instance variable being made available, which won't be if you're loading the partial in any other controller than the cards_controller.
--
Fix
The way to fix this is to either populate the #card instance variable in the application controller (as described by edymerchk), or to pass the raw value through the locals hash of the partial call:
This will allow you to use the card local variable in your partial:
#app/views/controller/_form.html.erb
<%= form_for card do |f| %>
-
Alternatively, you could also set the #card instance variable, as recommended in another answer:
#app/controllers/application_controller.rb
Class ApplicationController < ActionController::Base
before_action :set_card
private
def set_card
#card = Card.new
end
end

Dynamic data in rails partial view

I've just started to use Partials in my rails application, at the moment i have the following code in my application.html.erb
<%= render 'categories/categorieslist' %>
This links to _categorieslist.html.erb in my views/categories/ folder
At the moment this partial contains hard coded hyperlinks
<ul class="unstyled">
<li style="padding-bottom:5px">Item A»</li>
<li style="padding-bottom:5px">Item B»</li>
</ul>
My aim is to have these categories coming from the database, e.g
<ul class="unstyled">
<% #categories.each do |category| %>
<li style="padding-bottom:5px"><%= category.name %> » </li>
<% end %>
</ul>
I have tried adding a categorieslist method in the categories controller e.g
def categorieslist
#categories = Category.all
respond_to do |format|
format.html # index.html.erb
format.json { render json: #categories }
end
end
but this is not being called by the partial (and i don't feel this is even the correct way to do it), and is showing the error
NoMethodError in Store#index
on the line <% #categories.each do |category| %>
My question is how do i pass into the partial in the application.html.erb file, the categories object that usually would come from a controller method in the categories controller?
Any help would be great.
You can send locales with your partial call in your view and pass variables to that partial.
For example (this is a partial shortcut):
Your view from which you call the partial
<%= render 'categories/categorieslist', :all_categories => #categories %>
Your partial categories/_categorieslist.html.erb (note there is no # with the variable)
<ul class="unstyled">
<% all_categories.each do |category| %>
<li style="padding-bottom:5px"><%= category.name %> » </li>
<% end %>
</ul>
For further information (and the long version), see 3.4.4 Passing Local Variables in the Rails Guides.
I'd use a collection for this:
<%= render 'categories/categorieslist', :collection => #categories, :as => :category %>
This renders a collection of items. In this case, all the categories. You can also pass it a custom name with the :as => .
Then in your partial you only include the stuff you want the items in the collection to render:
<li style="padding-bottom:5px"><%= category.name %> » </li>
The -ul- isn't included as it would be rendered multiple times. You'll need to wrap it around your render tag.
The result is the same as the suggestion #timbrandes outlined, (check out docs he linked to).
I've heard :collection gives you performance improvements.
http://rails-bestpractices.com/posts/38-use-render-collection-rails-3-when-possible
That's not the right way to do it, and your question is rather confusing.
I'd say that you still have to read a rails book. You seem to be still a bit too fresh
Anyway, controller methods usually represent http requests. And they are invoked accordingly to what is defined in the config/routes file. Views (*.erb) do not usually invoke controller methods. If they do so, they do it through an ajax request.
Data is passed from actions to the views through controllers instance variables.
If you want to invoke any methods within the views, they should be defined in helpers. Still, the only data they will manipulate is the one passed from controllers as instance variables.

Resources