How to Implement ajax pagination with will_paginate gem

前端 未结 6 876
遥遥无期
遥遥无期 2020-12-02 10:19

I am using will_paginate gem in my ROR project to show the records in pages.

I want the next page to be loaded without reloading the whole

相关标签:
6条回答
  • 2020-12-02 10:35

    Additional to previous answer.

    string of code:

    $(".sort_paginate_ajax th a, .sort_paginate_ajax .pagination a").on("click", function(){
    

    replace with string:

    $(".sort_paginate_ajax").on("click", ".pagination a", function(){
    

    The onclick event applied only to the elements that already exist. So, for new appears links, need delegate click event from parent ".sort_paginate_ajax" to childs ".pagination a".

    0 讨论(0)
  • 2020-12-02 10:36

    Hey if you want to paginate products then try this:

    app/controllers/products_controller.rb

    def index
      @products = Product.paginate(page: params[:page], per_page: 10)
    end
    

    views/products/index.html.erb

    <div class = "sort_paginate_ajax"><%= render 'products' %></div>
    

    views/products/_products.html.erb

    <% @products.each do |product| %>
    # your code
    <% end %>
    <%= will_paginate @products %>
    

    views/products/index.js.erb

    $('.sort_paginate_ajax').html("<%= escape_javascript(render("products"))%>")
    

    assets/javascripts/application.js

    $(function() {
      $(".sort_paginate_ajax th a, .sort_paginate_ajax .pagination a").on("click", function(){
        $.getScript(this.href);
        return false;
      });
    });
    

    So first we are calling paginate method on Product for all products, here an offset will be set according to params[:page] and limit will be set by per_page options. Also we are rendering products partial which will be rendered every time when other page is opened.

    In partial call on @products that you want, and then will_paginate method is applied on @products, it also creates params[:page] that is used in controller.

    Now in response to ajax request render content of div having class sort_paginate_ajax with the partial that we created.

    Also to make request ajax request, on document load capture script of all a tags in div.sort_paginate_ajax, and return false.

    Now pages will be called with ajax request

    I hope this would help you.

    0 讨论(0)
  • 2020-12-02 10:37

    Create a new helper (ex. app/helpers/will_paginate_helper.rb) with the following content:

    module WillPaginateHelper
      class WillPaginateJSLinkRenderer < WillPaginate::ActionView::LinkRenderer
        def prepare(collection, options, template)
          options[:params] ||= {}
          options[:params]['_'] = nil
          super(collection, options, template)
        end
    
        protected
        def link(text, target, attributes = {})
          if target.is_a? Fixnum
            attributes[:rel] = rel_value(target)
            target = url(target)
          end
    
          @template.link_to(target, attributes.merge(remote: true)) do
            text.to_s.html_safe
          end
        end
      end
    
      def js_will_paginate(collection, options = {})
        will_paginate(collection, options.merge(:renderer => WillPaginateHelper::WillPaginateJSLinkRenderer))
      end
    end
    

    Then in your view use this tag for ajax pagination:

    <%= js_will_paginate @recipes %>
    

    Remember that the pagination links will include existing params of the url, you can exclude these as shown below. This is standard will paginate functionality:

    <%= js_will_paginate @recipes, :params => { :my_excluded_param => nil } %>
    

    Hope that solves your problem.

    Update 1: Added a explanation of how this works to the original question where I posted this solution.

    Update 2: I've made it Rails 4 compatible with remote: true links and renamed the helper method to js_will_paginate.

    0 讨论(0)
  • 2020-12-02 10:42

    You could just use JQuery:

    Controller:

    def index
      @posts = Post.paginate(:page => params[:page])      
    
      respond_to do |format|
        format.html
        format.js
      end
    end
    

    Then in index.html.erb:

    <div id="post-content", posts: @posts >
      <%= j render 'post_content' %>
    </div>
    

    In partial '_post_content.html.erb':

    <%= will_paginate @posts %>
    <% @posts.each do |post| %>
      <p><%= post.content %></p>
    <% end %>
    

    pagination.js:

    $(document).ready(function() {
      $('.pagination > a').attr('data-remote', 'true');
    });
    

    index.js.erb:

    $('#post-content').html("<%= j render 'post_content' %>")
    

    Make sure to add the

    $('.pagination > a').attr('data-remote', 'true');
    

    again in your JS template (index.js.erb), so it sets the links data-remote again when the Ajax runs.

    0 讨论(0)
  • 2020-12-02 10:46

    I want to expand on Pierre's answer.

    If you are using materialize sass with will_paginate_materialize then you will have an initializer to style the pagination links.

    gem 'will_paginate', '~> 3.1.0'

    gem 'will_paginate-materialize', git: 'https://github.com/mldoscar/will_paginate-materialize', branch: 'master'

    So, in order to get the remote true to work on the links rendered by the will_paginate_materialize initializer I did this:

    will_paginate_helper.rb

    module WillPaginateHelper
      class WillPaginateJSLinkRenderer < WillPaginate::ActionView::LinkRenderer
      end
      def js_will_paginate(collection, options = {})
        will_paginate(collection, options.merge(:renderer =>WillPaginateHelper::WillPaginateJSLinkRenderer))
      end
    end
    

    I changed the will paginate materialize initializer to look like this:

    will_paginate_materialize.rb

    MaterializePagination::MaterializeRenderer.module_eval do
      def page_number(page)
        classes = ['waves-effect', ('active' if page == current_page)].join(' ')
        tag :li, link(page, page, :rel => rel_value(page)), class: classes
      end
    
      def prepare(collection, options, template)
        options[:params] ||= {}
        options[:params]['_'] = nil
        super(collection, options, template)
      end
    
      protected
      def link(text, target, attributes = {})
        if target.is_a? Fixnum
          attributes[:rel] = rel_value(target)
          target = url(target)
        end
    
        @template.link_to(target, attributes.merge(remote: true)) do
          text.to_s.html_safe
        end
      end
    end
    
    WillPaginate::ViewHelpers::LinkRenderer.class_eval do
      def symbolized_update(target, other, blacklist = nil)
        other.each_pair do |key, value|
          key = key.to_sym
          existing = target[key]
          next if blacklist && blacklist.include?(key)
    
          if value.respond_to?(:each_pair) and (existing.is_a?(Hash) or existing.nil?)
            symbolized_update(existing || (target[key] = {}), value)
          else
            if value.instance_variable_defined?(:@parameters)
              value = value.instance_variable_get(:@parameters)
            end
            target[key] = value
          end
        end
      end
    end
    

    In my view I left the renderer in the will paginate link the same:

    = will_paginate results, renderer: MaterializePagination::Rails

    0 讨论(0)
  • 2020-12-02 10:50

    To follow on from @Pierre's excellent answer re creating a helper, I've seen some follow up questions regarding updating the views (a problem I initially had). @Pierre follows up with that problem by stating that you need to create a js response. Here's what I did after creating the helper:

    in my show.html.erb

    <div id="see-comments">
        <%= render @comments %>
    </div>
    <div id="pagination-comments">
        <%= js_will_paginate @comments %>
    </div>
    

    I created another file called show.js.erb (so two formats for show)

    // to update comments    
    $("#see-comments").html("<%= j render @comments %>");
    // to update the pagination links
    $("#pagination-comments").html('<%= js_will_paginate @comments %>');
    
    0 讨论(0)
提交回复
热议问题