Oh!Coder

Coding Life

Kaminari Gem简介

| Comments

今天介绍的gem名为Kaminari,其功能和名为will_paginate的gem类似,都是实现页面分页的效果。

简介

基于Scope和Engine,干净的,功能强大的,可自定义的以及复杂的,针对现代web应用框架和ORM的分页器。

特点

干净

不会对全局ArrayHashObjectAR::Base造成污染。

简单易用

只需要bundle一下gem,就可以在model上正确使用了。无需配置。不需要在model或helper上做任何定义。

简单的基于scope-based的API

所有方法都是基于方法链的。就像Rails 3中那样。不需要特殊的集合类或任何其它分页变量,相对应的只需要一个通用的AR::Relation实例。所以,你自然可以在分页器scope之前或之后插入任何其它条件。

可自定义的以engine为基础的I18n-aware的helper

作为整个分页的helper,基本上就是链接和非链接的集合,Kaminari通过内置Engine的partial模版进行对每一页进行渲染。所以,你可以简单的修改它们的行为,样式或任何可以覆盖的partial模版。

ORM以及template engine无关

Kaminari支持多个ORM(ActiveRecord,Mongoid),多个web framework(Rails,Sinatra,Grape),以及多个template engine(ERB,Haml,Slim)。

现代化

分页helper默认输出HTML5的<nav>标签。另外,helper支持Rails 3的常规Ajax。

安装

把下面这行添加进Gemfile:

1
gem 'kaminari'

然后执行bundle:

1
% bundle 

用法

基础查询

  • 获取第七页的用户(默认per_page是25)
1
User.page(7)
  • per scope

对每一页显示更多用户(改变per_page数值)

1
User.page(7).per(50)

注意每个perscope并不是直接定义在model中,而是定义在page的scope上。这么做也是有原因的,你永远都不可能在不指定page数目情况下使用per_page

记住per内部使用了limit,并且覆盖limit任何之前的设置。并且如果你想要得到所有请求记录的数量,需要使用total_count方法:

1
2
3
4
User.count                    # => 1000
a = User.limit(5); a.count    # => 5
a.page(1).per(20).size        # => 20
a.page(1).per(20).total_count # => 1000
  • padding scope

偶尔你需要铺垫一个不是多页大小的记录数。

1
User.page(7).per(50).padding(3)

注意padding scope并不是直接定义在model上的。

通用配置选项

你可以使用Kaminari.configure方法对下面的默认值进行配置。

1
2
3
4
5
6
7
8
9
default_per_page  # 25 by default
max_per_page      # nil by default
max_pages         # nil by default
window            # 4 by default
outer_window      # 0 by default
left              # 0 by default
right             # 0 by default
page_method_name  # :page by default
param_name        # :page by default

有一个好用的生成器会在config/initializers目录下生成配置文件。运行下面的生成器命令,然后编辑生成文件。

1
% rails g kaminari:config
  • 更改 page_method_name

你可以把方法名称为page的地方改成bonzoplant或者任何你喜欢的名字,为的是更好的与已经存在的page方法或相关的方法或级联方法或其它任何你的model上定义的page方法进行更好的匹配。

为每一个model配置默认的per_page数值

  • paginates_per

使用下面的DSL你可以为每一个model指定默认的per_page

1
2
3
class User < ActiveRecord::Base
  paginates_per 50
end
  • 为每一个model配置最大per_page数值

  • max_paginates_per

你可以使用如下的DSL对每一个model指定最大per_page。如果通过’per’ scope指定的变量大于这个变量,那么max_paginates_per就会代替这个变量成为默认设置。此值默认为nil,这也就意味着强制执行任何最大’per_page’数值。

1
2
3
class User < ActiveRecord::Base
  max_paginates_per 100
end

Controller

  • page参数在params[:page]

通常情况下,你的controller代码看起来像是这个样子:

1
@users = User.order(:name).page params[:page]

View

  • 同样老的helper方法

只需要调用paginate帮助方法:

1
<%= paginate @users %>

这将会围绕着HTML5的<nav>标签渲染一些?page=N分页链接。

Helper

  • paginate帮助方法
1
<%= paginate @users %>

这行代码会输出几个类似于« First ‹ Prev ... 2 3 4 5 6 7 8 9 10 ... Next › Last »的分页链接。

  • 指定“内部窗体”大小(默认为4)
1
<%= paginate @users, :window => 2 %>
  • 指定“外部窗体”大小(默认为0)
1
<%= paginate @users, :outer_window => 3 %>

如果总共20页,这行代码输出样式看起来应该是1 2 3 4 ...(snip)... 17 18 19 20

  • 外部窗体可以通过指定leftright(默认为0)
1
<%= paginate @users, :left => 1, :right => 3 %>

如果总共20页,这行代码会输出类似于1 ...(snip)... 18 19 20

  • 更改(:param_name)参数名的链接
1
<%= paginate @users, :param_name => :pagina %>

这会每一个链接上修改查询参数名称。

  • 额外的链接参数(:params
1
<%= paginate @users, :params => {:controller => 'foo', :action => 'bar'} %>

这行代码会修改每行链接的url_option。:controller以及:action通常情况下会作为key。

  • Ajax链接(超级简单,但可以非常好的工作!)
1
<%= paginate @users, :remote => true %>

这行代码或许可以把data-remote="true"添加到所有链接内。

  • 指定一个可替换的view目录(默认是kaminari/
1
<%= paginate @users, :views_prefix => 'templates' %>

上面这行代码会搜索app/views/templates/kaminari路径。这个可选项让分页模版/样式的A/B测试变得更简单,同一时间使用new/old模版可以像cell一样与其它gem进行集成。

  • link_to_next_pagelink_to_previous_page帮助方法
1
<%= link_to_next_page @items, 'Next Page' %>

简单渲染了一个link指到下一页。这个对于创建一个类似Twitter的分页特性非有帮助。

  • page_entries_info帮助方法
1
<%= page_entries_info @users %>

这里渲染了一条带有数字展示和全部内容的帮助信息。

  • rel_next_prev_link_tags帮助方法
1
<%= rel_next_prev_link_tags @users %>

这里为head渲染了next和prev链接标签。

I18n和label

默认的’first’,’last’,’previous’,’…‘和’next’ label被存储在engine的I18n的yaml文件中,通过I18n的API进行渲染。你可以为你的国际化应用程序切换每一个I18n.locale的label值。key和默认数值紧随其后。你可以在Rails.root/config/locales目录下,通过添加一个YAML文件进行覆盖。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
en:
  views:
    pagination:
      first: "&laquo; First"
      last: "Last &raquo;"
      previous: "&lsaquo; Prev"
      next: "Next &rsaquo;"
      truncate: "&hellip;"
  helpers:
    page_entries_info:
      one_page:
        display_entries:
          zero: "No %{entry_name} found"
          one: "Displaying <b>1</b> %{entry_name}"
          other: "Displaying <b>all %{count}</b> %{entry_name}"
      more_pages:
        display_entries: "Displaying %{entry_name} <b>%{first}&nbsp;-&nbsp;%{last}</b> of <b>%{total}</b> in total"

自定义分页帮助方法

Kaminari包含了一个便捷的template生成器。

  • 编辑你的分页器

首先运行生成器,

1
% rails g kaminari:views default

然后在app/views/kaminari/目录下编辑partial。

  • 为Haml用户

Haml的template通过为生成器添加-e haml可选项继而可以生成(当默认template_engine设置到Haml,会进行自动引用)。

1
% rails g kaminari:views default -e haml
  • themes

除了默认的那个以外,生成器有能力从额外的几个地方获取一些模版案例(github.com/amatsuda/kaminari_themes),这些将会帮助你创建一个非常好看的分页器。

1
% rails g kaminari:views THEME

要看所有可用的样式列表,可以看下样式的托管站点,或者干脆不要为生成器指定THEME参数。

1
% rails g kaminari:views
  • 多种样式

为了在一个单页面应用中使用多个样式,可以在app/views/kaminari/创建一个目录,然后把自定义的模版文件移动到此目录下。

1
2
3
4
% rails g kaminari:views default (skip if you have existing kaminari views)
% cd app/views/kaminari
% mkdir my_custom_theme
% cp _*.html.* my_custom_theme/

接下来,当调用paginate方法时,将会引用那个目录:

1
<%= paginate @users, :theme => 'my_custom_theme' %>

如果当前样式没有展示出来或者从未进行指定过,kaminari将会使用view中默认包括的gem样式。

分页一个通用的Array对象

Kaminari提供了一个Array包装器类,为paginate在view中的帮助方法适配一个通用的Array对象。然而,paginate帮助方法并不能自动处理Array对象(这是有意这么设计的)。Kaminari::paginate_array方法把Array对象转换成可分页的Array,并且接受一个page参数的方法。

1
@paginatable_array = Kaminari.paginate_array(my_array_object).page(params[:page]).per(10)

可以通过可选的Hash指定total_count值。当处理类似于RSolr的搜索结果中的count数值中有一个不同count数值的Array-ish对象时或许会很有用,或者是你需要生成一个自定义分页。例如:

1
@paginatable_array = Kaminari.paginate_array([], total_count: 145).page(params[:page]).per(10)

创建友好的URL和缓存

因为page参数和Rails 3路由,你可以很简单的生成SEO和用户友好的URL。对于任何你喜欢分页的资源,只需要在routes.rb中添加如下代码:

1
2
3
resources :my_resources do
  get 'page/:page', :action => :index, :on => :collection
end

如果你正在使用Rails 4以后的版本,可以通过使用concern很简单的定义路由:

1
2
3
4
5
concern :paginatable do
  get '(page/:page)', :action => :index, :on => :collection, :as => ''
end

resources :my_resources, :concerns => :paginatable

上面这段代码创建的URL类似于/my_resources/page/33而不再是/my_resources?page=33。前面的URL就是一个友好的URL,但是它还有其它已经添加的好处…

因为page参数现在是一个URL的段,我们可以充分利用Rails的页面缓存

注意:在这个例子中,我已经把路由指向了我的:index action。你或许已经在controller中定义了一个分页action - 你应该指向:action => :your_custome_action替换。

Sinatra/Padrino的支持

自从0.13.0版本以来,kaminari开始支持Sinatra或基于Sinatra扩展的框架。

为了在这些框架中使用kaminari和它的helper,需要做如下引入,

1
require 'kaminari/sinatra'

或编辑 gemfile:

1
gem 'kaminari', :require => 'kaminari/sinatra'

这行代码只是打开了model层面的特性,例如Model#pageModel#per。如果你想使用view层面的helper,请在Sinatra或Padrino的app中精确指定register 的helper:

1
register Kaminari::Helpers::SinatraHelpers

或者,你还可以实现自己的helper

更多

嗯,更多详情还是请自行查看原文档

Comments