So I've got pagination with Kaminiari set up on my app, which is a forum. When someone replies to the thread, I want to direct them to the last page of the thread. It seems like it'd be easy enough to hard code the logic to get the last page based on what I'm using for record numbers, but is there a built in method to get the last page?
In my current version of kaminari (v0.12.4) the following works:
users = User.page(params[:page])
last_page = users.num_pages
num_pages is defined in https://github.com/amatsuda/kaminari/blob/master/lib/kaminari/models/page_scope_methods.rb.
If you want to add a last_page method, I suggest the following:
module Kaminari
module PageScopeMethods
def last_page
num_pages
end
end
end
It seems that this thread has an answer.
You can write a method to achieve this if not already present in Kaminari . This should be present since Kaminari also renders the page numbers for navigation.
Say, #records is list of db records where you performed #records.page(1) to show the get the current set of records,
The last page number is defined by (#records.total_count / per_page.to_f).ceil .
For other people's sake, I'll share what worked for me.
Associations
Conversation has_many Messages
Message belongs_to Conversation
In Conversation show page, I want to paginate every 10 messages
messages = conversation.messages.page(params[:page]).per(10)
last_page = messages.total_pages
Then I want to create a link to this show page which will show me the last page. I just made a helper method
def create_link_to_last_page(conversation)
content_tag :div do
link_to("Show", url_for(controller: 'conversations', action: 'show', id: conversation.id, page: last_page))
end
end
Related
Problem has been answered -- see post below
I have a CableReady channel 'Current' which dynamically shows a feed of 'Posts'. I can get the posts to appear individually just fine, but I can't work out how to remove them from the channel individually.
This input of Posts to the channel is controlled in the PostsController.rb like so:
def postshow
#post = Post.find(params[:id])
cable_ready["current"].insert_adjacent_html(
selector: "#current#{#video.id}",
position: "afterbegin",
html: render_to_string(partial: "posts/post", locals: {post: #post})
)
cable_ready.broadcast
end
I've tried a remove method e.g. cable_ready["current"].remove(...
but this just removes all of the Posts in the channel in one go
I plan to use another method in my PostsController.rb to perform the remove:
def postremove
#post = Post.find(params[:id]
...[code to remove the post here]
end
I don't want to remove the Post from the DOM entirely because the feed is dynamic and I want them to be able to show in the feed again at some point.
Edited: Further explanation of wanted behaviour
Imagine the feed to be like twitter, new posts appear at the top. But these posts only appear for a certain number of seconds. You can can also rewind the feed to a certain point. So a post that was taken off the feed can appear again at the top if you rewind the time.
Any ideas or suggestions of other tactics are appreciated, thanks
Since you don't want to remove the post from the DOM then perhaps one solution would be to simply hide the post. You can use the method below to set a CSS property.
Alternatively if you use a CSS framework you could just add a class via:
Thanks to Roland Studer for this answer:
The problem was due to the partial not having a proper identifier. The outer-most div of the partial now looks like this:
<div id="<%= dom_id(post)%>" ... >
and the controller method for removing the Post now looks like this:
def postremove
#post = Post.find(params[:id])
cable_ready["current"].remove(
selector: "[data-id=\"#{#post.id}\"]"
)
cable_ready.broadcast
end
Magic! :)
So I'm trying to re-create GitHub version control for let's say posts. I've found a way to re-create an original post using duplicate AND another method to create a new post based on the original. Cool.
My issue is being able to display both the original and the new on the same page.
What I've attempted thus far is to just rely on the show method with having:
def show
#post = Post.find(params[:id])
end
Then in the view have in the form a checkbox to allow a user to select multiple posts, click a submit, and a new page renders displaying both side by side. Preferably showing the differences between the two but that's a wish list as I deal with this first.
Actually could I just simply do?:
def other_show
#post = Post.where(params[:id])
end
I also added in status as a boolean to help on the view for marking the checkbox. Would I then need to put something in the other_show method about the status?
If you want to "recreate" some sort of version control I suggest you use something like the audited. Instead of building your own. From your example and comments it seems you don't have a clear relation between all related (versions of) posts.
Using this gem, each change to the Post content (for example, if configured properly) would be stored as an audit.
Showing the differences is a different problem. That's usually called a diff and you can find gems that do it for you, for example: diffy
To show 2 different entities on one page you need to give posts_controller both ids.
Declare your show method like this:
def show
#original = Post.find(params[:id])
#compared = Post.find(params[:compared_id])
end
Correct route to this method will look like this:
/posts/:id?compared_id=:another_id
# Example: /posts/1?compared_id=2
To construct such a link in your view, you need to declare link_to method like this:
<%= link_to '1 <> 2', post_path(#post, compared_id: '2') %>
If you want to have a page where user can check 2 checkboxes for certain posts, you'll need to construct such href via Javascript.
But in fact I wouldn't suggest you to modify show method for such a task. It is better to use show method only for showing one entity from database. You can create another method, e.g. compare and pass both parameters there.
def compare
#original = Post.find(params[:original_id])
#compared = Post.find(params[:compared_id])
end
In routes.rb
resources :posts do
get 'compare', on: :collection
end
It will give you helper compare_posts_path, which will lead to /posts/compare and you'll need to pass original_id and compared_id to it, like this:
<%= link_to 'Compare', compare_posts_path(original_id: 'some_id', compared_id: 'some_another_id') %>
It will result to
/posts/compare?original_id=some_id&compared_id=some_another_id
I have the following ActiveAdmin model:
ActiveAdmin.register Job do
controller do
def scoped_collection
Job.all
end
end
This works just fine, the pagination and the links. However, I need to use a more complex query to list the count of Job views for each job. So I changed my scoped collection to this:
ActiveAdmin.register Job do
controller do
def scoped_collection
Job.joins('left join jobView on job.Id = jobView.JobId')
.select('job.Id, job.Title, job.CreatedDate, job.DeadLineDate,
job.IsClosed, job.IsPublic, job.IsApproved, count(1) as totalViews')
.group('job.Id, job.Title, job.CreatedDate, job.DeadLineDate,
job.IsClosed, job.IsPublic, job.IsApproved')
end
end
The second works. But the pagination links are not showing anymore. I can even see that the pagination itself works, because if I insert in the browser a query string like '?page=2' it paginates correctly to page 2. The only real problem is the missing links for changing pages on the bottom of the page.
Anyone knows what's going on? Thanks guys.
EDIT
I've found out that the problem happens because, if you do something like:
model = MyModel.select(field1, field2)
model.count
model.count will break because rails is executing a query like
select count(field1, field2) from MyModel
However, count just accepts 1 parameter. And ActiveAdmin paginations probably does a count.
The solution would be to use size instead count. But how to change this behavior in AA?
Edit 2 - Found ugly solution
Since I use a left join in my query, the number of rows will always be equal to the left table count. So, I did a count on my Job table inside my Index Controller and then I passed this count to a partial where I build the pagination links by myself:
/admin/job.rb:
index do
...
#jobsForPagination = Job.all.page(params[:page]).per(8)
render partial: "paginate", locals: {resource: #jobsForPagination}
end
/views/admin/jobs/_paginate.html.erb:
<%= paginate resource %>
8 is the activeadmin config.per_page.
If someone finds a better solution or has any comments, please reply.
Thank you!
I've got a table full of information at the moment, Ideally i need the information from a database table to be viewed via a link.
I only have the controller and some standard html (the html is just a h1 tag at the moment)
The HTML will be standard throughout like a template.
The way i'm seeing what i want in my head is the users will get a link which would be events_prev/{{id from DB here}} and depending on the ID the information on the page will be populated from the corrisponsing DB Row
Heres my controller
class Events::EventsPrevController < ApplicationController
def index
#events = Event.where(id: id)
end
def show
render :nothing => true
end
end
Sorry if its super confusing.
Welcome to rails.
Ok, there's a couple of things that will get you in the right directions. Firstly, you REALLY need to do a little reading to understand how the controller and the routes and the views are linked together in rails, that'll help you tons.
But moving on to your specific issues:
Parameters:
All data passed via a url (get, post, put, doesn't matter the method) is available in the controller in an array object called params - So that means when want to access the data the user submitted, you'll use something like
#event = Event.where(id: params[:id])
Routes:
It looks like you're trying to use the index page. In rails index is a RESTful route which generally points to a collection of model objects. The show route will point to an individual object so you should instead make your link point to the show path instead of the index path.
You can view the routes available on a model on a command line using:
bundle exec rake routes
An example of what your routes might look like:
prev_events GET /prev_events(.:format) prev_events#index
POST /prev_events(.:format) prev_events#create
new_prev_event GET /prev_events/new(.:format) prev_events#new
edit_prev_event GET /prev_events/:id/edit(.:format) prev_events#edit
prev_event GET /prev_events/:id(.:format) prev_events#show
PATCH /prev_events/:id(.:format) prev_events#update
PUT /prev_events/:id(.:format) prev_events#update
DELETE /prev_events/:id(.:format) prev_events#destroy
Link
Based on the routing table, you now should see that the link you need your users to click on might look like this (given that event is your object:
<%= link_to event.title, event_path(event.id) %>
or shortcutted
<%= link_to event.title, event %>
View
For the view this is entirely dependent on the data in the Event model. Since the data is stored in #event you'll simple use the attributes on the event model to render the html however use like, e.g.
<h3><%= #event.title %></h3>
<span><%= #event.start_time %></span>
You should read up on Rails controllers: by default the action index is used to show all of the records and what you're talking about should belong to the show action. The default routes take care of the id passing to your show action.
Index action is mean to show list of items in view and Show action is used to show a single item.
what you are doing in index is actually mean to be in show action.
Reason:
#events = Event.where(id: id)
this line will give u a single record in every case it means it should be in Show action.
your code should look like:
def show
#event = Event.find(params[:id])
[your logic: what you want to do with that #event]
end
I have a social feed.
If the user scrolls down a lot it is annoying to the user that by liking/commenting he is redirected to the top of the page instead of in the same part of the page to where he had scrolled to.
Is there any way to do this? Otherwise I'll just use paginate to make the pages smaller, which isn't ideal because that also takes away from user friendliness.
class ActivitiesController < ApplicationController
def index
#activities = Activity.order("created_at desc").where(user_id: current_user.following_ids)
end
def show
redirect_to(:back)
end
end
I've been on a roll with questions please check them out if you have time :)
Assuming that is being redirected to the top of the page because the page is being reloaded after a comment/favorite, you could try performing these actions using ajax instead.
This way, the page won't reload and you can modify the DOM to reflect the user's actions with javascript.
Here's some more information on ajax in rails:
http://guides.rubyonrails.org/working_with_javascript_in_rails.html
First generate anchors in your page by giving them ids. For example:
<div id="activity5">
..
</div>
Then in your controller, redirect to that part by using an anchor option:
redirect_to(request.env["HTTP_REFERER"] + '#activity5')
Note: redirect_to(:back) is the same as redirect_to(request.env["HTTP_REFERER"])
Having said that, using Javascript and AJAX is probably a better option.