pagination_links_remoteを作ってみる。

Ajaxを利用して、リスト表示の並べ替えや検索を処理するようにしたため、pagination_linksを使えない状態だった。並べ替えや検索で表示条件をアレンジしても、ページ番号をクリックすると、保持していた表示条件がリセットされてしまうのだ。
表示条件を保持するためには、ページ番号のリンクをクリックした時に、条件を保持しているフォームも送信する必要がある。つまり、pagination_linksで生成されるページ番号に、link_to_remoteが作り出す機能を与えたい。How to Paginate With Ajaxのページを参考に、以下のようにやってみた。(上記ページで紹介されている一番最後のパターンでやってみた。)

app/helpers/application_helper.rbへの追記

# Methods added to this helper will be available to all templates in the application.
module ApplicationHelper
  include LoginEngine
  include UserEngine
end

# 以下pagination_links_remoteの追記
module ActionView
module Helpers
module PaginationHelper
  def pagination_links_remote(paginator, options={}, html_options={})
    name = options[:name] || DEFAULT_OPTIONS[:name]
    #params = (options[:params] || DEFAULT_OPTIONS[:params]).clone

    pagination_links_each(paginator, options) do |n|
      options[:url][name] = n
      link_to_remote(n.to_s, options, html_options)
    end
  end
end # PaginationHelper
end # Helpers
end # ActionView
  • pagination_linksのソースとほとんど一緒だ。
  • do...endブロック内が、pagination_links_remoteのために置き換えたコード。
  • デフォルト値は以下。
DEFAULT_OPTIONS = { :name                 => :page, 
                    :window_size          => 2, 
                    :always_show_anchors  => true, 
                    :link_to_current_page => false, 
                    :params               => {} }
      • module ActionView、module Helpers、module PaginationHelperのように、三重にネストするコードの意味が知りたい...。

app/views/softwares/list.rhtmlへの追記

<%= pagination_links_remote @software_pages, 
        :update => "list_update", 
        :submit => "store", 
        :url => {:action => 'list_update'} %>
  • ビューでpagination_links_remoteを実際に利用すると、上記のような書式になる。
  • link_to_remoteと似たような書式だけど、urlのオプションはアクションまで。それ以下のページはpagination_links_remoteが設定してくれるから。


すべての流れを理解してないが、なんとかpagination_links_remoteもどきが出来た!

      • 以下は、自分でpagination_linksのソースを読んだ時のコメント
# 全9ページの表示例(デフォルトオプションのままで、現在のページは5)
#   1 ... 3 4 5 6 7 ... 9
# 
# 以下デフォルトオプションのキーの意味
# :name                 ページを表現するパラメーター名
# :window_size          前後のページを表示する範囲(もし1なら 1 ... 4 5 6 ... 9 と表示される)
# :always_show_anchors  最初と最後のページを常に表示するかどうか?(もしfalseなら 3 4 5 6 7 と表示される)
# :link_to_current_page 現在のページにリンクを設定するかどうか?
# :params               その他のパラメーター
module ActionView
  module Helpers
    module PaginationHelper
      # デフォルトオプションの定義
      unless const_defined?(:DEFAULT_OPTIONS)
        DEFAULT_OPTIONS = {
          :name => :page,
          :window_size => 2,
          :always_show_anchors => true,
          :link_to_current_page => false,
          :params => {}
        }
      end

      def pagination_links(paginator, options={}, html_options={})
        name = options[:name] || DEFAULT_OPTIONS[:name]
        params = (options[:params] || DEFAULT_OPTIONS[:params]).clone
        
        pagination_links_each(paginator, options) do |n|
          params[name] = n
          link_to(n.to_s, params, html_options)
        end
      end
      # pagination_linksでは、do...endブロックをpagination_links_eachに渡して処理する。
      # pagination_links_eachでは、yieldの部分で上記do...endブロックを実行する。
      # yield(引数)の部分は、以下のコードに置き換えられる。
      #   params[:page] = 引数
      #   link_to(n.to_s, params, html_options)
      # link_toをlink_to_remoteに置き換えれば、pagination_links_remoteができるかも...。
      def pagination_links_each(paginator, options)
      ### 利用する変数を準備する
        # オプションにデフォルトを加える
        options = DEFAULT_OPTIONS.merge(options)

        # 現在のページにリンクを設定するかどうか?
        link_to_current_page = options[:link_to_current_page]

        # 最初と最後のページを常に表示するかどうか?
        always_show_anchors = options[:always_show_anchors]

        # 現在のページ
	current_page = paginator.current_page

        # ページ番号として表示するpaginatorオブジェクトの配列(3 4 5 6 7のページ配列)
        window_pages = current_page.window(options[:window_size]).pages

        # 1ページ以下の場合の処理
        return if window_pages.length <= 1 unless link_to_current_page

        # 最初と最後のページを取得
        first, last = paginator.first, paginator.last

      ### 以下で実際に 1 ... 3 4 5 6 7 ... 9 のリンクの集合を作る
      ### 3つのパートに分けて処理している
        # 「1 ... 」の部分を生成
        html = ''
        if always_show_anchors and not (wp_first = window_pages[0]).first?
          html << yield(first.number)
          # 上記yieldを実際に置き換えると、以下と同等
          # html << link_to(first.number.to_s, :page=>first.number, html_options)
          html << ' ... ' if wp_first.number - first.number > 1
          html << ' '
        end

        # 「3 4 5 6 7」の部分を生成
        window_pages.each do |page|
          if current_page == page && !link_to_current_page
            html << page.number.to_s
          else
            html << yield(page.number)
            # 上記yieldを実際に置き換えると、以下と同等
            # html << link_to(page.number.to_s, :page=>page.number, html_options)
          end
          html << ' '
        end

        # 「 ... 9」の部分を生成
        if always_show_anchors and not (wp_last = window_pages[-1]).last? 
          html << ' ... ' if last.number - wp_last.number > 1
          html << yield(last.number)
          # 上記yieldを実際に置き換えると、以下と同等
          # html << link_to(last.number.to_s, :page=>last.number, html_options)
        end

        html
      end

    end # PaginationHelper
  end # Helpers
end # ActionView