BackboneでPagination(ページャー機能)を実装

 

※ 2013-9-11 追記

Backbone.SimplePaginator というプラグインにして Github 上に公開しました。

 

BackboneでPagination(ページャー機能)の実装をしたいとなると、 backbone.paginatorという多機能なプラグインがあるのですが、多機能故に指定するオプションなどが多いので、向いていない場面もあります。

 

今回もうすこしシンプルでサクッと使えるプラグインが欲しかったので、backbone.paginatorのコードを参考に必要最低限の機能のプラグインを自前で作ってみました。

App.Collections.Paginator = Backbone.Collection.extend({

  currentPage: 1,
   
  perPage: 6,

  nextPage: function() {
    if (this.currentPage < this.information.totalPages) {
      this.currentPage = ++this.currentPage;
      this.pager();
    }
  },

  previousPage: function () {
    if (this.currentPage > 1) {
      this.currentPage = --this.currentPage;
      this.pager();
    }
  },

  goTo: function(page) {
    if (page !== undefined){
      this.currentPage = parseInt(page, 10);
      this.pager();
    }
  },

  goToFirst: function() {
    this.goTo(1);
  },

  pager: function() {
    var self = this
      , perPage = self.perPage
      , start = (self.currentPage - 1) * perPage
      , stop = start + perPage;

    if (!self.origModels) {
      self.origModels = self.models;
    }

    self.models = self.origModels.slice();
    self.reset(self.models.slice(start, stop));
  },

  info: function() {
    var self = this
      , currentPage = self.currentPage
      , totalPages = Math.ceil(self.origModels.length / self.perPage)
      , info;

    info = {
      currentPage: currentPage,
      perPage: self.perPage,
      totalPages: totalPages,
      lastPage: totalPages,
      previous: false,
      next: false
    }

    if (currentPage > 1) info.previous = currentPage - 1;
    if (currentPage < totalPages) info.next = self.currentPage + 1;

    info.pageSet = self.getPageSet();

    return info;
  },

  getPageSet: function() {
    var self = this
      , totalPages = Math.ceil(self.origModels.length / self.perPage)
      , pages = [];

    for (var i = 1, l = totalPages; i <= l; i++) {
      pages.push(i);
    }

    return pages;
  }

});

 

特徴としては、

・Collectionの拡張にすることで、Viewにロジックを書かなくて済む

・クライアント側で処理するので、サーバ側でPagination機能を実装する必要がない(静的なJSONファイル等にも対応できる)

・1ページに表示したいモデルをmodelsとしてもつので、ページ毎にchangeイベントが発火する

あたりでしょうか。

 

簡単な使い方ですが、まずperPageには1ページの表示数を指定しておきます。

PaginationさせたいCollectionは、以下のようにPaginatorを継承しておくようにします。

App.Collections.MovieList = App.Collections.Paginator.extend({

  initialize: function() {
    this.fetch();
    this.goToFirst();
  },

  ...

});

これでロジック側の準備は完了。

 

Viewはこんな感じ。初期化時にPaginatorを継承したCollectionを渡してあげます。

App.Views.Pagination = Backbone.View.extend({

  template: JST['pagination'],

  events: {
    'click a.prev': 'goToPrev',
    'click a.next': 'goToNext',
    'click a.page': 'goToPage'
  },

  initialize: function() {
    this.collection.on('reset', this.render, this);
  },

  goToPrev: function(e) {
    e.preventDefault();
    this.collection.previousPage();
  },

  goToNext: function(e) {
    e.preventDefault();
    this.collection.nextPage();
  },

  goToPage: function(e) {
    e.preventDefault();
    var page = $(e.target).text();
    this.collection.goTo(page);
  },

  render: function() {
    this.$el.html(this.template(this.collection.info()));
    return this;
  }

});

テンプレートはこんな感じ。

<span class="pages">
  <% if (currentPage != 1) { %>
    <a href="#" class="prev"><<</a>
  <% } %>
 
  <% _.each(pageSet, function(p) { %>
    <% if (currentPage == p) { %>
      <span class="page selected"><%- p %></span>
    <% } else { %>
      <a href="#" class="page"><%- p %></a>
    <% } %>
  <% }); %>
 
  <% if (lastPage != currentPage && lastPage != 0) { %>
    <a href="#" class="next">>></a>
  <% } %>
</span>

 

あとはCSSで調整すれば、以下のような素敵なPaginationが出来上がります。

f:id:se__i:20130421011050p:plain