Reputation: 6897
In my Rails 4 app, I have a Post
and a Calendar
model: a calendar
has_many
post
s and a post
belong_to
a calendar
.
In the post
show.html.erb
view, located at /posts/:id
, I want to allow users to navigate back and forth between the posts of the calendar to which the current post belongs to, with a "previous post" button and a "next post" button.
Here are my routes:
resources :calendars do
resources :posts, shallow: true
end
end
I know I will have something like that in my post
show.html.erb
view:
<% if @calendar.posts.count > 1 %>
<%= link_to "< Previous", @previous_post %> | Post Preview | <%= link_to "Next >", @next_post %>
<% else %>
Post Preview
<% end %>
So far, in my PostsController
, I came up with:
def show
@calendar = Calendar.find_by_id(@post.calendar_id)
@posts = @calendar.posts
@previous_post = @post.previous
@next_post = @post.next
end
However, I am struggling to come up with the right definition of the previous
and next
methods (that you can see in the PostsController
code above).
These methods must allow me to find — respectively — the post that is right before or right after the current post in @calendar.posts
How can I achieve that?
Upvotes: 0
Views: 2089
Reputation: 1809
Your solution with next
and previous
commands relative to date is good, but doesn't work completely because you need to order
the results chronologically as well. The where
will filter the ones you don't want, but you need to make sure that the rest are in the order you desire.
So you'd have something like:
def next
calendar.posts.where("time > ?", time).order(:time).first
end
def previous
calendar.posts.where("time < ?", time).order(time: :desc).first
end
Edit: I'm assuming that time is a DateTime. If it is a Time ONLY without date information, you'll urgently want to change that to a DateTime field.
Upvotes: 4
Reputation: 6897
This is what I ended up doing:
def next
calendar.posts.where("id > ?", id).first
end
def previous
calendar.posts.where("id < ?", id).last
end
def show
@calendar = Calendar.find_by_id(@post.calendar_id)
@previous_post = @post.previous
@next_post = @post.next
end
This is not an ideal solution, but it is working.
—————
UPDATE:
Because posts must be displayed in chronological order, I had to change the above code to:
#post.rb
def next
calendar.posts.where("time > ?", time).first
end
def previous
calendar.posts.where("time < ?", time).last
end
However, this is not working perfectly, as you can see on this gif:
It is almost as if the previous button still works based on post id
and not time.
I did restart my server in case that was the problem, but it did not fix it.
Any idea how I can improve on this code?
—————
UPDATE 2: based on Richard Seviora's answer, I also tried this:
#post.rb
def next
calendar.posts.where("date > ? AND time != ?", date, time).order(:time).first
end
def previous
calendar.posts.where("date < ? AND time != ?", date, time).order(time: :desc).first
end
Still not working as expected.
—————
Upvotes: 0
Reputation: 34328
Not a very ideal kind of solution, but in your case, should work and give you the previous
and next
posts of a @post
from a @posts
array.
Add get_next_previous_posts
helper method and use it in your controller's show
method:
def show
@calendar = Calendar.find_by_id(@post.calendar_id)
@posts = @calendar.posts
@previous_post, @next_post = get_next_and_previous_posts(@posts, @post)
end
private
def get_next_and_previous_posts(posts, current_post)
next_post = posts.detect { |p| p.id > current_post.id }
prev_post = posts.reverse.detect { |p| p.id > current_post.id }
[prev_post, next_post]
end
Upvotes: 1
Reputation: 701
It sounds like you need some pagination. will_paginate or kaminari gems should do the trick (I prefer kaminari).
Upvotes: 0