読者です 読者をやめる 読者になる 読者になる

Rails Webook

自社のECを開発している会社で働いています。Rails情報やサービスを成長させる方法を書いていきます

RailsでAngularJSを使ってTodoアプリを作成 - 7. AngularJS + Kaminariでページネーション機能を実装

AngularJS Rails中級 連載

f:id:nipe880324:20150112194806p:plain

「RailsでAngularJSを使ってTodoアプリを作成」の連載7回目です。
前回は、AngularJSとRansackを一緒に使い検索機能を実装しました。

今回は、AngularJSとKaminariを使い、ページネーションを実装します。

動作確認

  • Rails 4.2.0
  • AngularJS 1.3.8
  • Bootstrap 3.3.1
  • UI Bootstrap 0.12.0
  • ransack 1.6.2
  • kaminari 0.16.1

目次

  1. kaminariのインストール
  2. ページネーションの実装



1. kaminariのインストール

Kaminariをインストールします。

Gemfilekaminariを追加します。

# Gemfile

# ページネーション機能を追加
gem 'kaminari'


bundle installを実行します。

bundle install



2. ページネーションの実装

Todoリスト画面の一番下にページネーションを追加します。

<!-- app/views/templates/todo_list.html.erb -->
     ...

     </ul>
   </div>

  <!-- 追加箇所 開始 -->
  <pagination total-items="totalTodos" ng-model="currentPage" ng-change="search()"></pagination>

  <br />
  <!-- 追加箇所 終了 -->

   <a href="dashboard" class="btn btn-default">戻る</a>
 </div>

ページネーションには、UI Bootstrapを使っています。
toal-itemsには「アイテムの合計数」、currentPageには「現在のページ番号」、ng-changeは「番号がクリックされたときのイベント」を定義します。また、1ページあたり10アイテムがデフォルトになっています。

詳細は、「Pagination - UI Bootstrap」を参照してください。


次に、totalTodos(アイテムの合計値)をサーバー側から取得し、設定するようにします。

# app/assets/javascripts/controllers/TodoListCtrl.coffee
  # 初期データを用意するメソッド
  $scope.init = ->
    ....
    # データを取得する(GET /api/todo_lists/:id => Api::TodoLists#show)
    $scope.list = @todoListService.find($routeParams.list_id, (res)-> $scope.totalTodos = res.totalTodos)
 ...

findメソッドの第2引数にコールバック関数を記載することで、totalTodosに値を設定しています。
これは、ng-resourceのメソッドはサーバーからの結果をまたず次の処理を行うように設計されているので、ng-resourceのブロック内で処理を行わないと正しい値が設定されないためです。



次にkaminariの1ページのアイテム数のデフォルトが25ですが、UI Bootstrapのページネーションのデフォルトの10と合わせておきます。

# app/models/todo.rb
class Todo < ActiveRecord::Base
  paginates_per 10
  ...
end


そして、pageメソッドを呼び出し1ページのアイテム(10件)しか返さないようにします。
また、totalTodosというキーで「アイテムの合計数」も返すようにします。

# app/views/api/todo_lists/show.json.jbuilder
json.name  @todo_list.name
json.todos @todo_list.todos.page(1) do |todo|
  json.id          todo.id
  json.description todo.description
  json.completed   todo.completed
end
json.totalTodos  @todo_list.todos.count


今度は、検索メソッドを修正します。検索ボタンを押したときだけでなく、ページネーションのリンクをクリックしたときも呼ばれるので、サーバー側に現在のページ番号(currentPage)を送信するようにしておきます。

# app/assets/javascripts/controllers/TodoListCtrl.coffee
  $scope.search = ->
    # Ransackに対応したparamsを作成する
    params = {
      'q[description_cont]' : $scope.descriptionCont,
      'q[completed_true]'   : $scope.completedTrue,
      'page'                : $scope.currentPage
    }

    # init()と同様にtotalTodosに正しい値が入るように第2引数にコールバック関数を渡す
    $scope.list = @todoService.all(params, (res)-> $scope.totalTodos = res.totalTodos)


上記でコールバック関数を渡しているので、Todoサービスクラスのallメソッドにハンドラーを追加しておきます。
また、allメソッド内のqueryメソッドが配列を返さないようになるので、isArray: falseも追加する必要があります。

# app/assets/javascripts/services/TodoService.coffee
constructor: (todoListId, errorHandler) ->
  @service = $resource('/api/todo_lists/:todo_list_id/todos/:id',
    { todo_list_id: todoListId },
    { query: { isArray: false }, update: { method: 'PUT' }})
   @errorHandler = errorHandler

all: (params, successHandler) ->
  @service.query(params,((list)->
    successHandler?(list)
    list),
    @errorHandler)


では、TodosControllerのindexメソッドの修正をします。

# app/controllers/api/todos_controller.rb
def index
  @q            = @todo_list.todos.ransack(params[:q]).result # ransackの検索
  @todos        = @q.page(params[:page])  # kaminariのページネーション
  @total_todos  = @q.count
end


そして、index.josn.jbuilderを作成し、アイテムの合計数をtotalTodosで返すようにします。

# app/views/api/todos/index.json.jbuilder
json.name @todo_list.name
json.todos @todos do |todo|
  json.id          todo.id
  json.description todo.description
  json.completed   todo.completed
end
json.totalTodos  @total_todos


では、サーバーを起動して、動作を確認します。
10件以上だとページネーションが分けられています。検索やページネーションのリンクを押しても問題なく動きます。
f:id:nipe880324:20150123213547j:plain:w480



以上です。