Paginate without a gem Next, Previous, buttons for

2019-02-27 10:52发布

问题:

I've been searching for a solution to my problem for over a week now. I have an assignment that I lost 10 pts on due to no next/prev functionality and ran out of time. I still want to figure this out though.

I have Created a short single page site with rails generate scaffold Ripple name:string message:text url:string that shows an index of 10 most recent posts displays (name, message, created_on, link_to "show"). I still have to create a next, previous, newest, oldest links in view to show next 10, prev 10.... results. My code.

app\controllers\ripple_controller.rb

class RipplesController < ApplicationController  
  before_action :set_ripple, only: [:show, :update]  
  before_action :restrict_destroy_edit, only: [:edit, :destroy]   
  before_filter :set_page   
  helper_method :link_name_to_url, :next_button, :previous_button, :newest_button, :oldest_button, :is_next_page_available, :is_previous_page_available

  RIPPLES_PER_PAGE = 10

  def index   
    @ripples = Ripple.order(:id).limit(RIPPLES_PER_PAGE).offset(@page * RIPPLES_PER_PAGE) 
  end
  #All my show, new, destroy, edit, create ....

  def next_button

  end

  def previous_button

  end

  def newest_button

  end

  def oldest_button

  end

  def is_next_page_available?

  end

  def is_previous_page_available?

  end

  def set_page 
    @page = 5 
  end
private
...

\app\views\ripples.html.erb

<table>
  <thead>
    <tr>
      <th>Name</th>
      <th>Message</th>
      <th>Posted</th>
      <th>Show Ripple</th>
    </tr>
  </thead>

  <tbody>
    <% @ripples.each do |ripple| %>
      <tr>
        <td><%= link_name_to_url ripple %></td>
        <td><%= truncate(ripple.message, length: 50) %></td>
        <td><%= ripple.created_at.strftime("%B %d, %Y %l:%M %P") %></td>
        <td><%= button_to 'Show', ripple, :method => "get" %></td>
      </tr>
    <% end %>
  </tbody>
</table>

<br>

<div id = "nav">
  <button><%= link_to 'Newest' %></button>
  <button><%= link_to 'Previous 10 Ripples' %></button>
  <button><%= link_to "Next 10 Ripples" %></button>
  <button><%= link_to 'Oldest' %></button>
  <button><%= link_to 'New Ripple', new_ripple_path, class: "button", method: :get %></button>
</div>

And I've tried calling methods in Model but keep getting undefined method "next" for #<Class:0xb4eabd0c> error on next and previous.

app\models\ripple.rb

class Ripple < ActiveRecord::Base
  default_scope -> {order(created_at: :desc)}
  validates :name, :message, presence: true
  validates :url, allow_blank: true, format: {
  with: URI::regexp(%w(http https)),
      message: "Must be a url starting with http:// or https://"
  }

  def next
    Ripple.order(:id).limit(10).offset((@page - 1) * 10)
  end

  def previous
    Ripple.order(:id).limit(10).offset((@page + 1) * 10)
  end
end

How would I implement next and previous using the order().limit().offset and maybe use @page to keep track of where I'm at in the ActiveRecord. Maybe something like

def next_button
  @page -= 1
end

that I can call in index "<%= link_to Next 10" next_button %> either way I'm out of ideas that might work.

Thanks for any help.

回答1:

A few things here. Firstly, controller methods should be used with matching routes. It's a request cycle where you click a button on your app, it makes a request to your server, then your server response with information.

Your next_button method is not going to work when you put it as a helper_method this way. In order to make your controller work, you should have routes that match to your controller method. Do a rake routes in your command line. You need to see something like this in your route.rb file

get 'ripple/next', 'ripple#next'

More about routes: http://guides.rubyonrails.org/routing.html#controller-namespaces-and-routing

And in your controller, you could have

def next
  page = params[:page]
  params[:page] += 1
  @ripples = Ripple.find_ripples_on_page(page, PAGE_SIZE)
  render :index
end

Then in your erb view, your should be 'visiting' this particular route, not calling the method.

Secondly, you can not rely on class instance variable in your model. Instead, you should put it in your session or as part of a query string. As above controller code, I put it in session, which is a local storage. More about session: http://guides.rubyonrails.org/action_controller_overview.html#session

Finally, your model.

def find_ripples_on_page(page, PAGE_SIZE)
  Ripple.where( ... ) # you can figure this out, depending on if you want to rank by updated_at or other filter
end

Side note: helper_method in controller is for retrieving information not giving command to the controller. All this macro does is use class_eval and 'copy' the method you have in controller into the renderer.

If you can use a gem, use will_paginate.

http://guides.rubyonrails.org/routing.html#controller-namespaces-and-routing



回答2:

How about this?

class RipplesController < ApplicationController  
  before_filter :set_page, only: [:index]
  RIPPLES_PER_PAGE = 10
  def index   
    @ripples = Ripple.order(:id).limit(RIPPLES_PER_PAGE).offset(@page * RIPPLES_PER_PAGE) 
  end
  private
    def set_page
       @page = params[:page].to_i || 0
    end
end

Then in the view links will be

<div id = "nav">
    <button><%= link_to 'Newest', ripples_path %></button>
    <button><%= link_to 'Previous 10 Ripples', ripples_path(page: @page - 1) %></button>
    <button><%= link_to "Next 10 Ripples", ripples_path(page: @page + 1) %></button>
</div>

All of this will be driven through the RipplesController#index method and will use URL's like /ripples?page=1, /ripples?page=2, etc.

These link_to calls will generate links with GET variables so that you can access them in the params hash and use them to drive the results returned.