I read the rails guide on associations and I have also gone through a bunch of questions here on SO and on reddit, but I am just not getting associations conceptually, I think. I have two specific issues I can use as examples from an app I am working on, but I am really just trying to understand what I am doing.
Models for these examples:
List
belongs_to :user
has_many :items
Item
belongs_to :list
belongs_to :user
User
has_many :lists
has_many :items
Issue 1: Rendering associations
I know that to show the related associations in a list I can do something like this in my view:
Users/show
<%= render #list %>
Lists/_list
<ol>
<%= list.name %>
<%= render list.items %>
</ol>
Items/_item
<li><%= item.name %></li>
This seems like magic to me. I know I am calling user.list and then list.item, but when there are multiple Lists, how does rails know to separate them with the associated Items underneath? In my head when I see this code I anticipate the following as output:
<ol>
List(a)
List(b)
List(c)
<li>item(a)</li>
<li>item(b)</li>
</ol>
What I am trying to do is render each List as a Tab and render the associated Items as the tab content. Ultimately, I have multiple different List type models I want as tabs and matching multiple different Item type models to render as tab content.
Edit: I got rid of my second issue, since the post was long and it could be a separate post. Restating the above, how can a take the above collection and render it successfully in tabs where Lists are the tabs and the associated Items render as the content?
I am running into issues if I use the code like this in my Users/show:
<div class="row">
<div class="col s12">
<ul class="tabs">
<%= render #lists %>
</ul>
</div>
<%=render #items %>
</div>
Lists/_list
<li class="tab col s3"><%= list.name%></li>
Items/_item
<div id="#tab_<%=list.id%>" class="col s12"><%= item.name %></div>
If I try this, then my item partial gives me an error, because I can’t call list.id, and it will just render all of my items without displaying them as an associated collection with the Lists. If I nest this and put everything after the <%=render lists %> inside the list partial, then the collections work, but the closing tags end up causing issues. I am not sure how to resolve this.
Passing collections to render has very little to do with associations per say - you should consult the Layouts & Rendering guide instead.
When you call render with a collection (an array of model instances) Rails creates a loop and uses the model name and conventions to find the correct partial for each member of the collection.
Rails does not care that the collection came from an association.
added:
Since the relationship between List and Item is bidirectional you can always access the list to which the item belongs by item.list:
# views/items/_item.html.erb
<div id="tab_<%= item.list.id %>" class="col s12">
<%= item.name %>
</div>
Also notice that I removed the # in the beginning of the ID since its an invalid character for an element id.
Related
In my view I'm generating a a number of restaurant menus that are divided into menu categories and within those categories are dishes. Right now I have my template setup as so:
<% #menus.each do |menu| %>
<h2><%= menu.name %></h2>
...
<% menu.categories.each do |category| %>
<h3><%= category.name %></h3>
...
<% category.dishes.each do |dish| %>
<p><%= dish.name %></p>
...
<% end %>
<% end %>
<% end %>
I was wondering what would be best practice when it comes to nesting multiple enumerables in this fashion? Aside from inserting this code into a partial, is there a better way to accomplish this without cluttering the view. Is it fine as is? Thanks.
Apparently, the way your models are setup (Menu has_many categories, Category has_many dishes etc.), if you are to show all those information in the same view, you have no other way rather than just looping through them and show the required data in your view which is fine, IMHO. I don't see any problem with that.
Just one tip, while looping through the associated model's data, you just need to be careful that you always have the data present for the associated model attributes, otherwise you may get undefined method 'method_name' for nil:NilClass error for some of them. To avoid that problem, you can also use try. e.g.
menu.try(:name)
category.try(:name)
dish.try(:name)
etc.
That way, your view will not explode if some or any one of the associated model has no data present (i.e. nil).
I am trying to make an app in Rails 4. I'm trying to figure out how to show associated attributes.
My objective is to have educators create projects. Students can then express interest in them. Once the project is created, the projects show includes a link to a form where students can express interest in participating, as follows:
<%= link_to 'Join this project', project_student_eoi_path %>
I have a model called Project.rb and another model called Project_student_eoi.rb.
The associations are:
Project has many project_student_eois
Project_student_eoi belongs to Project
In my project_student_eoi form, I want to show some of the attributes from the project table (project :title and :hero_image).
I found this problem on SO which is similar to mine and have tried the suggestion. It isn't working. http://stackoverflow.com/questions/15862365/displaying-attributes-for-associated-models-in-views
In my project_student_eoi view #form, I have:
<%= project_student_eoi.project.title if project.title %>
<%= project_student_eoi.project.hero_image.thumb if project.hero_image%>
I have actually got title and hero_image attributes - they display properly in the project # show page. I put the if statement in because that's the suggestion from the other SO post.
I get this error:
undefined local variable or method `project' for #<#<Class:0x007f9d44d50690>:0x007f9d44a52ce0>
I've tried putting '#' in front of the line to see whether that helps. I get this error:
<%= #project_student_eoi.project.hero_image.thumb if project.hero_image%>
In this case, I get this error:
NameError at /project_student_eois/new
undefined local variable or method `project' for #<#<Class:0x007f9d44d50690>:0x007f9d41d0f788>
I can't find any resources that help understand how to show associated attributes. I have a book called the Rails 4 Way - it talks a lot about defining associations but no clues about how to use them.
Project_student_eoi.rb:
class ProjectStudentEoi < ActiveRecord::Base
# --------------- associations
belongs_to :project
# belongs_to :student
# --------------- sweeps
# --------------- validations
# --------------- class methods
# --------------- callbacks
# --------------- instance methods
# --------------- private methods
end
I only added the if statement because it was a suggestion in the above SO post.
When I try:
<%= #project_student_eoi.project.title %>
<%= #project_student_eoi.project.hero_image.thumb %>
I get this error:
NoMethodError at /project_student_eois/new
undefined method `title' for nil:NilClass
The first set of comments below identify that I am trying to show existing project attributes in the form (a new action) for project_student_eoi. The form is not yet saved and doesn't yet exist. Is it best to create another view in projects, a nest the project student eoi form in that projects view, so that i can show the project details in a sidebar?
If this is a valid approach, I have added a new file to my project view folder called' student_eoi.html.erb. Inside that I have this code:
<div class="containerfluid">
<%= render 'static/deviselinks'%>
<div class="row">
<div class="col-md-10 col-md-offset-1">
<h1 class="header-project" style="margin-bottom:10%">
Express your interest
</h1>
<div class="col-md-7 col-md-offset-1">
<%= render 'project_student_eois/form' %>
</div>
<div class="col-md-3 col-md-offset-1">
<div class="uniboxtitle" style="background-color: #f1f1f1; padding:20px ">
<%= #project.title %>
<%= #project.hero_image.thumb %>
</div>
</div>
<div class="formminor">
<%= link_to 'Back', project_path %>
</div>
</div>
</div>
</div>
Then, in my projects#show page I have:
<%= link_to 'Join this project', student_eoi_path %>
When I try this, I get this error:
NameError at /projects/2
undefined local variable or method `student_eoi_path' for #<#<Class:0x007ff72bc87290>:0x007ff734312c60>
I think this might be because I have to add something to the projects controller to handle this view - I just don't know what that thing is. Is it a line nested in the show action, that says #student_eoi? I don't understand this process and don't know how to solve from here (if this is even a credible approach).
Also, i have restful routes. This will be the first time i've made a view that doesnt come from the scaffolding so not sure if i have to add something to the routes (which are currently just resources :projects).
My attempt at this is:
In my routes.rb, I now have:
resources :projects do
get 'projects/student_eoi' => 'projects/student_eoi', as => 'student_interest'
I don't understand what this get line means - I have copied from some other examples around SO but they are describing different issues (so don't know if I'm on the wrong track).
In my projects_controller.rb, I have:
def student_eoi
end
I don't know what to put in this definition. The idea for this new project show is to have a page with a sidebar that shows key project details and holds a form for a new project_student_eoi (which is the child resource).
Can anyone see what I need to do to get this working?
The form partial is in my project_student_eoi view folder. The reason that I have nested that form inside a view which is in my projects view folder is that I want to show the project title and image in a side bar next to the form. If the form is a new action, then it doesn't know how to find the project (when I try to reference the project from the project_student_eoi form). My attempts at doing it this way are above at the top of this post.
That's why I made a new file in the projects view folder called: student_eoi.html.erb.
In that, I have:
<div class="containerfluid">
<%= render 'static/deviselinks'%>
<div class="row">
<div class="col-md-10 col-md-offset-1">
<h1 class="header-project" style="margin-bottom:10%">
Express your interest
</h1>
<div class="col-md-7 col-md-offset-1">
<%= render 'project_student_eois/form' %>
</div>
<div class="col-md-3 col-md-offset-1">
<div class="uniboxtitle" style="background-color: #f1f1f1; padding:20px ">
<%= #project.title %>
<%= #project.hero_image.thumb %>
</div>
</div>
<div class="formminor">
<%= link_to 'Back', project_path %>
</div>
</div>
</div>
</div>
The gist is that the projects view knows which project it is talking about and nests the new form action from the project_student_eoi folder.
In my routes.rb, I now have:
resources :projects do
get 'projects/student_eoi' => 'projects/student_eoi', as => 'student_interest'
In my projects_controller.rb, I have:
def student_eoi
end
I don't know what to put in this definition. The idea for this new project show is to have a page with a sidebar that shows key project details and holds a form for a new project_student_eoi (which is the child resource).
When I try this, I get this error:
ActiveRecord::RecordNotFound at /project_student_eois/2
Couldn't find ProjectStudentEoi with 'id'=2
id 2 is the number of my project, not my projectStudentEoi. The line reference this error points to is:
def set_project_student_eoi
#project_student_eoi = ProjectStudentEoi.find(params[:id])
end
Can anyone see what I need to do to get this to work?
You want to show :title and :hero_image but only if they exist?
And this works?
<%= #project_student_eoi.project.title %>
If the above works, the error you're getting is due to the if statement. Change this:
<%= #project_student_eoi.project.title if project.title %>
To this:
<%= #project_student_eoi.project.title if #project_student_eoi.project.title %>
The error is because project is not a valid method or variable for the view itself.
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.
Im super new to programming and trying to get to grips with rails, I'm having trouble getting my head around how to route links to certain pages in my app.
I have a table of users reviews and venues, where a user has many reviews and a venue has many reviews.
The reviews are displayed as partials on the appropriate venues show page and each partial contains info on the user who wrote it.
review partial
<div class="review">
<% link_to #user do %>
<div class="reviewer_details">
<div class="reviewer_details_photo">
<%= image_tag review.user.avatar.url(:thumb) %>
</div>
<%= review.user.username %>
</div>
<% end %>
<div class="review_content">
<div class="review_partial_star_rating" style="width: <%= review.rating*20 %>px;"></div>
<h2 class="review_partial_title"><%= review.title %></h2>
</div>
<% if can? :update, review %>
<div class="review_partial_option">
<%= link_to 'edit', edit_review_path(review) %>
</div>
<% end %>
<div class="clearall"></div>
</div>
The line <% link_to #user do %> is redirecting to the current page (the venues show page), I want it to redirect to the users show page.
What am I missing here? and where can I read up on this to get it right? I'm confused as when to use #user, :user, user or User.
Thanks for any help its much appreciated!
This is a great start - looks like you just need to learn a bit more Ruby and you'll get what's going on here. You've got two questions, one about view helpers and the other about variables, symbols and constants. I'll take a stab at each.
In your case, the link_to tag should be used something like this:
<%= link_to user_path(#user) %>
Assuming that you're using default Rails routing, you're using the link_to helper to point to the users_controller#show method for #user.
The second part of your question is as follows:
User is a constant for all of the User objects you want to create. Think of it as a blueprint for all users. User.new creates an empty, unsaved user object for you to fill with the information you need.
user is a variable. It will only be available within the context of whatever you're working on. If you were to say user = User.new in your controller, that user object would not be available in your view.
#user is an instance variable. It works largely the same as standard variable, but if you were to define #user = User.new in your controller, you could use that object in the view.
:user is a user symbol. It's a little bit harder to define what Rails will use symbols for, but you don't need it in this situation.
I'd recommend that you check out Why's Poignant Guide to Ruby, or read some other intro books to learn a bit more about the structure of the language - it'll really help to bring clarity to why you'd want each of these.
Additionally, I use the www.railsapi.com for fast searching of the Ruby and Rails API.
I saw this post
Ruby on Rails - Awesome nested set plugin
but I am wondering how to do the same thing without using node? I am also wondering what exactly this node is doing in the code itself.
In my categories view folder i have _category.html.erb and a _full_categores_list.html.erb.
The _category.html.erb has the following code which is the sae way as the link above.
<li>
<%= category.name %>
<% unless category.children.empty? %>
<ul>
<%= render category.children %>
</ul>
<% end %>
</li>
The _full_categories_list.html.erb has the following code.
<ul>
<% YourModel.roots.each do |node| %>
<%= render node %>
<% end %>
</ul>
This code works perfectly fine. However, lets say hypothetically that I wanted to create duplicates of these files so instead of _full_categories_list.html.erb I was maybe making a _half_categories_list.html.erb which might do something a little different with the code.
If I use similar code like what i used above in the _full_categories_list.html.erb it will keep calling the _category.html.erb.
How can I show all the cats, sub cats, and sub sub cats by using _half_categories_list.html.erb and a file like _half_category.html.erb instead of _category.html.erb
The half category and full category are just names to indicate that I am doing something different in each file. I hope this makes sense. I want to basically duplicate the functionality of the code from the link above but use _half_category.html.erb instead of _category.html.erb because I'm trying to put different functionality in the _half_category.html.erb file.
First: there's a simpler way to write _full_categories_list.html.erb using render, with the :partial and :collection options.
<ul>
<%= render :partial => :category, :collection => YourModel.roots %>
</ul>
This is equivalent to the _full_categories_list.html.erb you wrote above.
roots is a named scope provided by awesome_nested_set. You can add more scopes to your models - for example a named scope called half_roots (see the link for information about how).
With this in mind, _half_categories_list.html.erb could be written as follows:
<ul>
<%= render :partial => :half_category, :collection => YourModel.half_roots %>
</ul>
You can then use _half_category.html.erb to render the categories in that special way you need.