Rendering collections within collection in Rails - ruby-on-rails

This is driving me crazy, I thought it should be something very simple but I've spent all day trying to figure it out and getting nowhere. I've looked all over, there's something I must be missing.
I want to render a partial for a collection, within a partial from a collection.
Eg. I have a collection of entries which I want to render in my feed partial, I want feed to come from a collection of feeds, such that the page displays all entries in all feeds.
How can I do this?
Something like:
controller:
#feeds = Feeds.all
allmyfeeds.html.erb
#feeds.each do |feed|
<%= render 'feed' %>
<% end %>
or
<%= render 'feeds', collection: #feeds %>
_feed.html.erb
<%= render 'items', collection: #items %>
_item.html.erb
<%= item.summary.html_safe %>
But I really don't know.

Partial naming conventions will really help you out here...
In allmyfeeds.html.erb (which i'm guessing is actually feeds/index.html.erb), instead of this...
#feeds.each do |feed|
<%= render 'feed' %>
<% end %>
You can simply call this...
<%= render #feeds %> # this will invoke _feed.html.erb for every feed in the collection, automatically
Inside your _feed.html.erb partial...
# Note: You may need to change `items` to something like `items/items` if it is not located in the same directory
<%= render partial: 'items', locals: { items: feed.items } %>
Then in your items partial, you will have access to items, which is a collection of items for that particular feed

Somehting like:
<% #feeds.each do |feed| %>
<%= render partial: 'feed', locals: { feed: feed } %>
<% end %>
In your _feed.html.erb:
<h1><%= feed.id %> </h1>
<% feed.entries.each do |entry| %>
<&= render partial: 'entry', locals: { entry: entry } %>
<% end %>
In your _entry.html.erb:
<h2><%= entry.id %></h2>
I'm assuming your Feed model has a has_many :entries association, if it's not the case (and what you want is to render the same collection of #entries in each feed), then you just need to pass the #entries collection to the _feed.html.slim partial, like:
render partial: 'feed`, locals: { feed: feed, entries: #entries }
and update your _feed.html.erb

Related

Rails 5 ActionView::Helpers capture method

I'm new in ruby on rails, and I want to practice it.
I'm stuck when I want to include a view into antoher view.
I want by doing that to have my posts into another view than of posts/index
posts/index
method:
def index
#Posts = Post.all
end
view:
<% #posts = capture do %>
<% #posts.each do |post| %>
<h3>
<%= post.title %>
</h3>
<p>
<%= post.content %>
</p>
<% end %>
<% end %>
pages/index
<h1> Index Of another pages </h1>
<%= #posts %>
If you want to force your index action to render another view, then go with follow code:
def index
#Posts = Post.all
render 'pages/index'
end
Correct me if I haven't get you
It sounds to me like you need to save the reusable view code as a partial, and render it all places it's required.
To use a partial, save it down with an underscore prefix, i.e. _posts.html.erb.
This can then be rendered using:
<%= render 'path/to/posts' %>
You'll likely need to pass in the posts variable to the partial, as in the following:
<%= render 'path/to/posts', posts: #posts %>
And the change your view to use posts rather than #posts.
Update:
The result of capture is assigned to #posts, although this variable still wouldn't be available in another template - rather to be used again on the same page
Based on what you're saying about the project's structure, it sounds like you'd need the following:
in app/views/posts/_posts.html.web
<% #posts.each do |post| %>
<h3>
<%= post.title %>
</h3>
<p>
<%= post.content %>
</p>
<% end %>
In both controllers' index action:
#posts = Post.all
In the posts/index view:
<%= render 'posts' %>
In the pages/index view:
<%= render 'posts/posts' %>
I don't want to confuse things, but Rails has a little magic in there where -
alternatively - you can define a partial _post.html.erb as follows:
<h3>
<%= post.title %>
</h3>
<p>
<%= post.content %>
</p>
And simply call <%= render #posts %> in each view. This would be the best 'Railsy' way of doing things.
Let me know how you get on!

Ruby on rails: Is it possible to nest partial calls/rendering like so:

Assuming that the "books/book" partial below is being called from multiple locations (like: author#show view, categogories#show view, categories#index view) and i only want the votes partial called from the author#show view. That is, i want the books collection returned from the books partial (#upvoted_books) to each render the "votes/vote" partail so that authors are able to vote on a book from their author#show view. Is the following code or a variation of it sensible?
<%= render partial: "books/book", collection: #upvoted_books do |upvoted| %>
<%= render partial: "votes/vote", locals: {category: book.category, book: upvoted } %>
<% end %>
p.s: I have tried this particular code here and it doesnt work in my project but i was just wondering if something similar to it could work but it doesnt throw any apparent error either. Maybe i was just getting the syntax a bit mixed up. I alos know a few other ways to solve the same problem but i was looking for something more elegant. Thanks in anticipationm of your creative responses.
update:
Thanks #Deep, for your very insightful solution. What I have done to solve the problem before is calling the line below from "books/book" partial:
<% if defined?(#upvoted_books) && #upvoted_books.include?(book) %>
<%= render partial: "votes/vote", locals: {category: book.category, book: upvoted } %>
<% end %>
That works but i'm not sure it has some significant code-smell or if its syntatically awkward. What do you think?
Final update:
#Deep these are the views you asked for: This is my category#show view :
`<h1>Topics#show</h1>
<p><%= #category.title %></p>
<%= render partial: "books/form", locals: {topic: #category, book: Book.new} %>
<%= link_to "edit category", edit_author_category_path(#category.author, #category) %>
<p> Books you have Authored </p>
<%= render partial: "books/book", collection: #category.books %>`
And this is my books/_book.html.erb partial now
`<% if defined?(upvoted_book) && current_author.books.include?(upvoted_book) %>
<h5>Created by <%= upvoted_book.category.author.name || upvoted_book.category.author.email %> on <%= upvoted_book.created_at %></h>
<br>
<%= link_to upvoted_book.title, category_book_path(upvoted_book.category, upvoted_book) %>
<% if policy(Vote.new).create? %>
<%= render partial: "votes/vote", locals: {category: upvoted_book.category, book: upvoted_book} %>
<br>
<% end %>
<% else %>
<h5>Created by <%= book.category.author.name || book.category.author.email %> on <%= book.created_at %></h>
<br>
<%= link_to book.title, category_book_path(book.category, book) %>
<%= link_to "Remove book", category_book_path(book.category, book), method: :delete, data: { confirm: "Are you sure you want to delete this book?" } %>
<% end %>`
As you can see, i refer to local variables book and upvoted_book in the partial. This is how i called the partial from the author#show view : `
<%= render partial: "categories/form", locals: {category: Category.new, author: #author} %>
<p> Categories and Books you have Writen: </p>
<%= render partial: "categories/category", collection: #categories %>
<p> Books from all authors that you have Upvoted: </p>
<%= render partial: "books/book", collection: #upvoted_books, as: :upvoted_book %>
<br> `
Thanks for you wonderful insight again.
As you need vote partial to be rendered on each book and only in a single view, so what you can do is:
<%= render partial: "books/book", collection: #upvoted_books, locals: { vote: true } %>
Now in your partial book write down:
if vote
<%= render partial: "votes/vote", locals: {category: book.category, book: upvoted } %>
end
And make sure from rest of the places where you are rendering book partial pass locals: { vote: false } or else it will throw error. Or what you can do in the partial is:
vote ||= false
Or instead of using if vote you can use if defined?(vote), so there would be no burden to pass it from other pages.
Also I doubt you have some wrong locals, not sure may be so correct them. But using this you can call vote partial on a single view.
Hope this helps.

Passing in variable to rails partial

Right now I have a rails partial that looks like this:
<%= render :partial => "/talk/partials/comment", :collection => #comments, :locals => {:votes => #votes} %>
I am passing in a collection of comments and another local variable.
That comment partial then goes right into using the comment variable and works fine.
I have since made another partial called '/talk/partials/comment_2014'. When I try this, I am getting the error undefined local variable or method 'comment'. From what I can gather, when I have a different partial name, something with the variable also changes. I would like to keep the same comment variable for the new partial ''/talk/partials/comment_2014'. How would I go about doing this?
Something I tried which did not work was the following:
<% #comments.each do |comment| %>
<%= render :partial => "/talk/partials/comment_2014", comment: comment, :locals => {:votes => #votes} %>
<% end %>
which did not work either.
You can do it this way
<% #comments.each do |comment| %>
<%= render "/talk/partials/comment_2014", comment: comment, votes: #votes %>
<% end %>
Or
<% #comments.each do |comment| %>
<%= render partial: "/talk/partials/comment_2014", locals: { comment: comment, votes: #votes } %>
<% end %>
Notice in the second way the comment is inside the locals.

Rendering a partial using specific controller variables

I am trying to play with Rails naming conventions as in here and render two different pages with different variables using one partial.
index
<%= render #events_future %>
current
<%= render #events_current %>
event controller
def index
#events_future = ...
end
_event.html.erb
<% #events.each do |event| %>
...
<% end %>
I get the undefined "each" method
Please point me in the right direction
I think the best thing to do here is to pass a locals to the partial _event.html.erb because the partial needs to display different objects like follows:
index
<%= render 'event', events: #events_future %>
current
<%= render 'event', events: #events_current %>
In the above two render statements, the events gets passed to the event partial as a local.
Then in your _event.html.erb you would do:
<% events.each do |event| %>
...
<% end %>
Do you have #events initialized in your controller?
I see that you have #events_future and #events_current, but if #events is not defined in the controller, your view wouldn't know what you are referring to.
If you want to reuse events for both future and current, use the following in each view
<!-- index.html.erb -->
<%= render 'event', events: #events_future %>
<!-- current.html.erb -->
<%= render 'event', events: #events_current %>
This renders the _event.html.erb partial and sets the events local variable. In _event.html.erb, use
<% events.each do |event| %>
<!-- do stuff -->
<% end %>
You have to pass the variable to the partial when you render it:
render :partial => 'event', :locals => {:events => #events_current} %>
render :partial => 'event', :locals => {:events => #events_future} %>
And then in your partial you do:
<% events.each do |event| %>
...
<% end %>

ruby on rails render deosn't find the file

I'm new to ruby on rails.
In views/events I have "_form.html.erb" which is rendered in "new.html.erb" by this code:
<%= render "form" %>
Now I want to render "_form.html.erb" in "index.html.erb" which is in the same folder(views/events).
But I get the error "missing template".
I guess I have to add some thing to controller, please help me to render form in other pages of views...
You "usually" don't render a form in an index action. Most form partials are setup semantically to expect a #my_resource, but if you're doing everything the rails way you're not going to have a instance variable during your index action. There's a number of ways you can do this but this is probably the quickest.
You probably have some collection (let's pretend you're using books) in your index action:
#views/books/index.html.erb
<% #books.each do |book| %>
...
<%= render "form" %>
...
<% end %>
You can just set an instance variable somewhere prior to rendering the form:
#views/books/index.html.erb
<% #books.each do |book| %>
<% #book = book %>
...
<%= render "form" %>
...
<% end %>
Another way to do it would be through passing in some locals to a partial. You'd have to change all of your references in _form to use a local variable instead. Then you can call render like this:
<%= render :partial => "form", :locals => {:book => book } %>
You can try
<%= render "events/form" %>
I had this problem before and this solved

Resources