`

一个经典的Rails AJAX 搜索 排序 和分页示例

阅读更多
写在前头
     绿色的部分是,这个介绍容易出问题部分的解释。本来就是面向零基础,才写的,所以不怕麻烦和琐碎。然而,如果你还是嫌太麻烦,或者你不希望了解细节的话。那么,我建议你直接点击这里,下载ajaxtable.rar的压缩包。在安装ruby和rails的环境下,到解压的目录下,运行ruby script/server直接看效果。
     压缩包里有paginate的plugin,数据库也不用配置因为是文件的。万一你的系统不认sqlite3,请下载sqlite3的rar,里面有个gem包安装就可以了。
    希望能够给有兴趣的,或者初学者带来帮助


   这是一个相当经典的(我认为^_^)用来展示,ajax调用排序,搜索和分页的示例。这个示例的特点是把过程写的相当的啰嗦,大妈专杀。以至于非常的好理解,非常的初级,非常的好用。

任务说明简介和重要提示

在这个示例教程理,我们将要使用Rails的AJAX,来实现对结果集的如下功能:

1. 分页
2. 排序,可以对结果集的任何一个属性进行ajax排序。
3. 查询,ajax检索

在线的示例演示,请访问如下网址:(还没有准备好...)

能够部分刷新的对页面的结果集进行更新,变得越来越普遍。对于Rails来说,天生的和ajax有着完美的支持。所以,对这项功能的实现相当容易。
示例中的代码, 很大部分来源于Rails Wiki的官方说明。特别是How to make a real-time search box with the Ajax helpers 和 How to paginate with Ajax.我只是把这些代码攒到了一起,不能用的时候,稍微改了一下变量名。以便看起来更不像抄袭。

注意啦,很重要:
我既不是Rails高手,也不是AJAX大拿。在这两个领域里,我都是拥有长期经验的菜鸟。所以这个示例是面对初学者和大妈们的。你会看到简介的代码,详尽的解释,但是缺乏高效规范。幸好,虽然,我的母语是汉语,但是难免还是有错误。
任何时候,请到我的博客告诉我。

应用安装和配置
在这个示例中,我们假设已经安装了当前的Rails环境(至少高于2.0)和支持数据库。在我们这里用Sqlite,当然你也可以很容易的使用其他的数据库代替。

框架生成:
我们要在目录下生成“skeleton”先:
rails ajaxtable
cd ajaxtable


在rails 2.0以后,分页的功能已经从Rails core中分离出来,成为一个独立的plugin叫作classic pagination。很不幸的很多开发者,认为will_paginate是更加流行的分页插件。但是,我并没有使用will_paginate整合我的示例。所以,我们还是首先,使用下面的命令安装classic pagination plugin
ruby script/plugin install svn://errtheblog.com/svn/plugins/classic_pagination

这里如果不能安装,请直接下载附件classic_pagination.rar解压缩放到verdor\plugin目录,就不用安装分页插件了。

鉴于,我们搭建的是一个非常基础的应用,所以,我们只有一个用来存储结果集的model和controller。我们将使用如下Rails的scripts生成
$ ruby script/generate model Item
$ ruby script/generate controller Item


数据库准备:

简单起见,我们选择sqlite做存储。这将意味着我们将描述文件放到Rails中,并且用它生成数据库。
更新数据库配置文件:config/database.yml如下:
development:
  adapter: sqlite3
  database: db/development.db

然后,我们将使用如下Rails migration 工具创建数据库如下:
ruby script/generate migration database_creation


这条语句将生成类似db/migrate/20090303130724_database_creation.rb的文件。
修改这个文件,重新定义数据库表结构
class DatabaseCreation < ActiveRecord::Migration

  def self.up
    create_table :items do |t|
      t.column :name, :string, :limit => 30
      t.column :quantity, :integer, :null => false, :default => 0
      t.column :price,  :integer, :null => false, :default => 0
    end
  end

  def self.down
    drop_table :items
  end

end

然后,运行如下:

rake db:migrate


这里如果,出现不能安装错误请下载附件sqlite3.rar,并放到ruby\bin目录下,执行
gem install -l c:\ruby\bin\sqlite3-ruby-1.2.3-mswin32.gem



用以创建表结构。接下来将加载数据

在db目录下应该有一个development.db的文件,这个文件就是存储sqlite数据。
你可以用如下的方法插入数据创建db/dump.sql如下:

BEGIN TRANSACTION;
INSERT INTO "items" VALUES(1, 'hoe', 3, 10);
INSERT INTO "items" VALUES(2, 'wheelbarrow', 2, 60);
INSERT INTO "items" VALUES(3, 'gherkin', 15, 3);
INSERT INTO "items" VALUES(4, 'batman', 1, 3000);
INSERT INTO "items" VALUES(5, 'fish sausage', 2, 8);
INSERT INTO "items" VALUES(6, 'sauerkraut', 9, 9);
INSERT INTO "items" VALUES(7, 'watering-can', 4, 13);
INSERT INTO "items" VALUES(8, 'dandelions', 78, 1);
INSERT INTO "items" VALUES(9, 'refrigerator', 12, 250);
INSERT INTO "items" VALUES(10, 'flying matches', 8, 145);
INSERT INTO "items" VALUES(11, 'broken accordion', 1, 18);
INSERT INTO "items" VALUES(12, 'savage whisper', 5, 7);
INSERT INTO "items" VALUES(13, 'hysterical snail', 8, 13);
COMMIT;


运行:
sqlite3 db/development.db < db/dump.sql

注意:
1.如果运行这个命令的时候,提示找不到sqlite3,请下载sqlite3的附件,放到ruby\bin\下,并且运行时,请指明路径
2.如果你使用的数据库是mysql请自己修改,insertinto的语句格式如下 INSERT INTO items VALUES (1, 'hoe', 3, 10);
那么,到现在为止,我们准备好了数据库和用到的文件

创建model

就像你应该知道的,Rails程序通常会分为三层。实际上我们已经创建了/models/item.rb文件。并且我们并不需要更改。
那么,看起来,我们的第一步编码工作还不算太困难。

创建view

我们应用将被分成两个部分,一个layout 另外一部分是view和partial。

Layout

Layout是页面模板用来容纳不同的几个views。Layout包含一些不变的元素例如:html的header和footer信息,导航和设计元素等。当然,这些功能完全没有能够在我们的示例中体现。因为,我们只有一个页面,
那么layout应该在app/views/layouts/item.rhtml,其中代码如下:

<html>
<head>
  <title>Ajax table manipulation attempt</title>
  <%= stylesheet_link_tag "style" %>
  <%= javascript_include_tag :defaults %>
</head>
<body>

<div id="content">
<%= @content_for_layout %>
</div>

</body>
</html>


在这段代码中,值得注意的是javascript_include_tag 将加载对应的javascript库,以便Rails可以得到AJAX功能支持。
@content_for_layout 部分,将会被生成的内容代替。

view部分

view将会把controller的结果展示。逻辑部分在一节详述。根据Rails配置原则,我们view将对应item controller的listaction所以我们的view在
app/views/item/list.rhtml.
该文件的内容如下:

 <h1>欢迎使用神奇的 items 列表</h1>

<p>我们的列表是Web2.0的经典产物。</p>

<p>但是请注意,这里可能有太多的bug.</p>

<h2>bug list如下</h2>

<p>
<form name="sform" action="" style="display:inline;">
<label for="item_name">Filter on item name  : </label>
<%= text_field_tag("query", params['query'], :size => 10 ) %>
</form>

<%= image_tag("spinner.gif",
              :align => "absmiddle",
              :border => 0,
              :id => "spinner",
              :style =>"display: none;" ) %>
</p>

<%= observe_field 'query',  :frequency => 2,
         :update => 'table',
         :before => "Element.show('spinner')",
         :success => "Element.hide('spinner')",
         :url => {:action => 'list'},
         :with => 'query' %>

<div id="table">
<%= render :partial => "items_list" %>
</div>


开始部分并没有什么复杂的,首先,是一段大妈专用的说明和一个检索输入框用以Filter。

然后,我们有一个id是spinner的隐藏image元素。这个image是用来AJAX有延迟调用的时候显示的(flash加载的滚动条)。当ajax的异步调用完成,可以显示数据时,这个image将再次隐藏。你可以从如下的网站得到更多的类似图片:
http://mentalized.net/activity-indicators/
从上面的网站中下载一个gif,并重命名为spinner.gif放到public/images下。

在接下来的observe_field代码部分是最常用的AJAX代码。它的含义大概是定期检查指定区域的内容,并且当内容有变化的时候响应。

其中,使用到的变量有如下含义:
update 参数描述将要更新的<div>或<span>的id
url    该参数,指定响应和处理action。就是定期触发什么方法。
with   该参数,用以给url中指定的action,传递参数。在这里我们将会把observed field的检索数据传给list action
before 该方法用以指定,当AJAX的异步调用处理中将怎么执行。
sucess 当ajax的异步调用成功后,执行什么操作。

实际的操作流程相当简单,当用户在queryfield的检索框内输入要查询的东西,observerd 就会检测到监视区域的内容变化,然后,生成AJAX的请求,异步调用通过url和with的发送给服务器。注意这时页面是整体和局部都不刷新的。当请求发送的时候,before定义的操作将被执行。在我们的示例中是显示spinner图片。当请求有回复的时候,sucess的操作将被执行,在我们这里是隐藏图片。

实际上,observe_field方面发送的请求参数如下:

<script type="text/javascript">
//<![CDATA[
new Form.Element.Observer('query', 2, function(element, value) {Element.show('spinner'); new Ajax.Updater('table', '/item/list', {asynchronous:true, evalScripts:true, onSuccess:function(request){Element.hide('spinner')}, parameters:'query=' + value})})
//]]>
</script>


我们花时间来看,没一个参数选项的具体含义,是因为我们很快会再用到这些几乎每一种ajax调用都要用到的参数,

创建controller

我们的controller应该可以根据请求类别和参数的不同处理多种的请求。
Item contoller将会非常简单,我们只实现一个list的action。其他CRUD(创建、读取、更新、删除)方法将不在本示例中展示

那么,controller的内容如下:
修改\ajaxtable\app\controllers\item_controller.rb

class ItemController < ApplicationController

  def list

    items_per_page = 10

    sort = case params['sort']
           when "name"  then "name"
           when "qty"   then "quantity"
           when "price" then "price"
           when "name_reverse"  then "name DESC"
           when "qty_reverse"   then "quantity DESC"
           when "price_reverse" then "price DESC"
           end

    conditions = ["name LIKE ?", "%#{params[:query]}%"] unless params[:query].nil?

    @total = Item.count(:conditions => conditions)
    @items_pages, @items = paginate :items, :order => sort, :conditions => conditions, :per_page => items_per_page

    if request.xml_http_request?
      render :partial => "items_list", :layout => false
    end

  end

end


本段代码的简单说明如下:

controller的唯一一个方法,用于处理各种不同请求。其中,items_per_page 参数用以定义每页显示数量。sort参数取决于同名的传入参数。出于安全考虑以此代替真正的字段名。sort 中的reverse参数用于保证再次点击的时候可以依序排列。
conditions 参数用来指定从query请求参数提供的检索条件。这个参数是类似SQL样式。
然后,我们指定@total变量用来存储符合conditions条件的记录个数。

最终,我们调用Rails的分页机制。我们需要关联分页到数据库:item,和一个可排序字段,一个符合conditions的检索条件,和一个每页的显示个数。分页机制就会返回,一个@items_pages 的对象用以分页显示。

Rails和Ajax使用XmlHttpRequest,这不同与普通的GET和POST请求。XHR的请求由javascript通过后台的Http调用触发,请求一个部分的Xhtml代码片段来更新部分的浏览器显示。这样的好处是不用重新加载整个页面。用户的使用体验,会因此变快。

那么接下来呢?

创建partial

partial是用来显示部分的页面。partial的设计初衷是为了复用,满足DRY(Don‘t Repeat Yoursel)的原则,当然,这里对于AJAX也非常有用。

这里我们使用partial更新部分页面,正好满足AJAX部分更新的要求。
Partial文件的名字总是下划线开头。我们的partialapp/views/item/_items_list.rhtml,内容如下

<% if @total == 0 %>

<p>No items found...</p>

<% else %>

<p>Number of items found : <b><%= @total %></b></p>

<p>
<% if @items_pages.page_count > 1 %>
Page&nbsp;:
<%= pagination_links_remote @items_pages %>
<% end %>
</p>


<table>
  <thead>
    <tr>
      <td <%= sort_td_class_helper "name" %>>
        <%= sort_link_helper "Name", "name" %>
      </td>
      <td <%= sort_td_class_helper "qty" %>>
        <%= sort_link_helper "Quantity", "qty" %>
      </td>
      <td <%= sort_td_class_helper "price" %>>
        <%= sort_link_helper "Price", "price" %>
      </td>
    </tr>
  </thead>
  <tbody>
    <% @items.each do |i| %>
    <tr class="<%= cycle("even","odd") %>">
      <td><%= i.name %></td>
      <td><%= i.quantity %></td>
      <td><%= i.price %></td>
    </tr>
    <% end %>
  </tbody>
</table>

<% end %>


分页的helpers文件

在开始的时候,我们有一个符合条件的记录数和设定的每页显示记录数的判断。如果,符合条件的记录数小于每页可以显示的记录数,则不用分页。
相反,我们就需要要显示分页信息,虽然,Rails已经有了处理和显示的机制。可是我们希望能够实现ajax分页。那么我们需要创建pagination的helpers。
helper方法是用来帮助生成和显示view的。目的是为了将显示和逻辑分离,当然一定程度的复用和代码重构。
我们的helper文件在app/helpers/item_helper.rb. 我们的view可以读取这个文件的任何一个方法。但是,如果我们如果,希望应用中的任何view都可以使用这个文件的方法,那么我们就需要把代码放到application_helper.rb.
内容如下:

def pagination_links_remote(paginator)
  page_options = {:window_size => 1}
  pagination_links_each(paginator, page_options) do |n|
    options = {
      :url => {:action => 'list', :params => params.merge({:page => n})},
      :update => 'table',
      :before => "Element.show('spinner')",
      :success => "Element.hide('spinner')"
    }
    html_options = {:href => url_for(:action => 'list', :params => params.merge({:page => n}))}
    link_to_remote(n.to_s, options, html_options)
  end
end

我们定义只有一个window_size的page_options的hash。这个参数标识当前页旁边的可以显示的页,如下如果window_size=1那么就会显示如下:
1 ... 5 6 7 ... 13
如果 window_size=2则显示
1 ... 4 5 6 7 8 ... 13

这样,我们就可以通过pagination_links得到对应的xhtml分页后的xhtml通过以上的连接。这的确可以用,然而,我们希望能够异步调用,实现AJAX的分页,所以我们重写pagination_links_each 方法
pagination_links_each 方法参数分析:
option和类似之前我们observe_field的参数,新的部分是params.merge,表示我们通过连接把将当前的请求参数代替之前的状态。
html_options是用来定义没有AJAX支持下的分页显示。以便分页机制在没有javascript支持下也可以用。

下面是实际的有两个页面市,第一个页面显示时的生成代码

<a href="/item/list?page=2" onclick="Element.show('spinner'); new Ajax.Updater('table', '/item/list?page=2', {asynchronous:true, evalScripts:true, onSuccess:function(request){Element.hide('spinner')}}); return false;">2</a>

sorting helpers

继续添加helpers

sort_td_class_helper代码如下:

def sort_td_class_helper(param)
  result = 'class="sortup"' if params[:sort] == param
  result = 'class="sortdown"' if params[:sort] == param + "_reverse"
  return result
end

作用是添加一个class="sortup"用以支持逆序排列

sort_link_helper.

def sort_link_helper(text, param)
  key = param
  key += "_reverse" if params[:sort] == param
  options = {
      :url => {:action => 'list', :params => params.merge({:sort => key, :page => nil})},
      :update => 'table',
      :before => "Element.show('spinner')",
      :success => "Element.hide('spinner')"
  }
  html_options = {
    :title => "Sort by this field",
    :href => url_for(:action => 'list', :params => params.merge({:sort => key, :page => nil}))
  }
  link_to_remote(text, options, html_options)
end


这个helper是上面pagination_links_remote的缩小版,它有两个参数:
text 用于显示字段的头和排序连接
param 和字段关联的请求参数
本段代码首先定义一个变量key用于保持param传递过来的参数。_reverse用于表示param是否正在排序中的参数。也就是说实现,第二次点击逆序。

接下来的代码定义了link_to_remote方法需要的参数,和paginateion_links_remote非常类似
option是哈希表,详情见上
html_options 也还是为了javascripte不支持下的功能实现。
下面是sort_link_helper以Quantity" 和 "qty" 作为 text 和 param 参数返回值的显示表格

最后,在用patial显示table的时候,如果希望能够,一行一个颜色,我们需要加入一个cycle的rails方法,来自动增加奇数和偶数的样式方法。

最后了

我们已经或多或少的看了,在这个演示中用到的所有技术细节。在这个过程的最后,通过如下url享用你的劳动成果。
如果,这个文档对你有那么半点帮助,我的时间就值得欣慰了。我再重复一下,有什么问题请在我的博客反馈。
谢谢


   
分享到:
评论
1 楼 zhaoningbo 2012-09-29  
非常感谢!正在找上手的例子,果断详细又明确!这个顶,必须有~~~~

相关推荐

    DataTables自行封装请求参数和返回数据的零耦合服务端分页示例

    封装请求参数(查询、排序、分页,不再需要data和dataSrc) 封装返回数据 自定义查询参数 服务器分页 自行控制和自定义遮罩效果 生成自定义效果的单元格(在线离线) 生成复选框单元格 响应复选框选择事件 生成操作...

    ajaxtable:用于表显示的Ajax包装器,用于排序和分页

    用于表显示的Ajax包装器,用于排序和分页 安装 所需文件已添加 &lt;link rel="stylesheet" href="css/ajaxtable.css"&gt; [removed][removed] //Then you can do $(".ajaxtable").ajaxtable(); 回复 该插件期望...

    DataTables自行封装请求参数和返回数据的零耦合服务端分页示例(PHP&JAVA)

    封装请求参数(查询、排序、分页,不再需要data、dataFilter和dataSrc) 封装返回数据 自定义查询参数 服务器分页 自行控制和自定义遮罩效果 生成自定义效果的单元格(在线离线) 生成复选框单元格 响应复选框选择...

    Asp.Net+Ajax+Jquery+Json无刷新分页

    1.无刷新分页控件,以Northwind数据库为示例, 2.支持多种分页样式选择,也可以自己修改源代码自定义 3.可以单击任意字段排序 4.MsSql数据库封装访问类,可以修改代码应用于其他数据库 5.继承自IHttpHandler接口...

    JqueryDataTablesServerSide:用于jQuery数据表的Asp.Net Core服务器端,具有分页和Excel导出的多列过滤和排序

    它提供了一种在服务器端实现动态多列搜索和排序以及分页和excel导出的快速方法。 这可以通过用简单的属性修饰模型属性来完成。注意:本教程包含AJAX GET和AJAX POST服务器端配置的示例。 警告:如果我们严格遵循...

    MVCWebCompany MVC简单示例

    3. 执行PageBreak脚本,这段脚本会在Company数据库中新建PageBreak存储过程,用于分页(这段脚本是从网上下载的基于Max的分页存储过程,但是排序方面有一些小bug,我已经修改); 4. 执行InsertData脚本,此脚本会向...

    AJAX基础概念、核心技术与典型案例(内涵动态实例)

    程序描述:本章模仿Google ig,使用Ajax技术实现一个个性化主页系统。通过拖拽方式设置主页,当完成拖拽窗口时,自动保存布局,使用户可以随时根据喜好来设置布局。 /login.jsp 登录页面 /homepage.jsp ...

    最新Python3.5零基础+高级+完整项目(28周全)培训视频学习资料

    第一个python程序 变量 字符编码与二进制 字符编码的区别与介绍 用户交互程序 if else流程判断 while 循环 while 循环优化版本 for 循环及作业要求 第2周 本节鸡汤 模块初识 pyc是什么 python数据类型 bytes数据...

    sortable-table:生成可排序的Polymer Web组件来自内联或AJAX JSON,JSON5和数组

    表示和值计算的单独参数输入本地或远程数据:服务器端分页支持AJAX主题引导程序ExtJS 4ESPN替代行/非表主题文章网格模式下的文章现场示例和文档替代行主题具有撤消功能的行编辑器行过滤附加参数资料格式DOM元素AJ

    ExtAspNet_v2.3.2_dll

    -修正extjs最新版本(v3.2.2)中的一个bug,如果下拉列表中存在两个相同的Text,则SelectedValue返回值永远是第一个Text的值(feedback:ben.zhou)。 -应用补丁#6593, #6621(feedback:vbelyaev)。 +修正IE7下Grid分页...

    快速开发框架有demo和使用说明

    荐一套快速开发框架,有Demo、完整的使用说明、示例源码 Demo本身是一套ssh开发范例 新功能:带排序、分页功能的列表、对ajax的支持 通过简单配置和编码,就可以实现查询、分页、分页排序列表、统计等功能。

    JAVA上百实例源码以及开源项目

    6个目标文件,EJB来模拟银行ATM机的流程及操作:获取系统属性,初始化JNDI,取得Home对象的引用,创建EJB对象,并将当前的计数器初始化,调用每一个EJB对象的count()方法,保证Bean正常被激活和钝化,EJB对象是用...

    图书推荐系统-基于Web,使用python的Django框架开发,数据使用Scrapy进行采集+源代码+文档说明

    自动补全搜索,当键入某一个字母时,(使用ajax)通过判断获得跟输入的最相似的10个字符串,然后在下方显示出来。 登录页面: √ 1.Gif验证码 2.第一次登录时填写邮箱,并通过邮箱发送邮箱验证码 √ 3....

    csp-595:Vanilla Java Web 应用程序,零 libsframeworks,没有 IDE,只有编译器和记事本。

    p, 添加ajax分页和排序。 p, 添加ajax搜索以在用户点击搜索/清除后加载产品列表 p, 添加ajax,在用户点击类别后加载产品列表。 p, 去除味道 j、为咖啡产品分配类别(口味) j,制作更多的物品和口味,包括图像...

    FineUI(ASP.NET UI控件) v4.1.3.rar

    为Grid增加ShowPagingMessage,并更新示例:表格控件-&gt;分页与排序-&gt;数据库分页。 增加示例:导航控件-&gt;工具栏与菜单-&gt;内联菜单(HideOnClick)。 表格的PageIndex有效性检查,更新示例:表格控件-&gt;分页工具条-&gt;改变...

    ExtAspNet v2.2.1 (2009-4-1) 值得一看

    --我们做了优化,现在要使一个Asp.net的按钮能够AJAX提交,你不需要做任何设置(PageManager的属性EnableAjax为true即可,这是默认属性)。 +PageManager的实例方法AddAjaxUpdateControl改名为...

    asp.net知识库

    一个时间转换的问题,顺便谈谈搜索技巧 .net中的正则表达式使用高级技巧 (一) C#静态成员和方法的学习小结 C#中结构与类的区别 C#中 const 和 readonly 的区别 利用自定义属性,定义枚举值的详细文本 Web标准和ASP...

    GigaTables 是一个 ReactJS 插件,可帮助 Web 开发人员处理应用程序和 CMS、CRM、ERP 或类似系统中的表格数据。

    GigaTables ReactJS 插件 GigaTables 支持以下功能:--ajax 数据处理/编辑(CRUD)、--经典分页或无限滚动、--列排序、--活单元格编辑、--通用搜索(通过所有列) , -- 离散(每列)搜索, -- ajax 文件上传, -- shft...

    java开源包1

    GWT Advanced Table 是一个基于 GWT 框架的网页表格组件,可实现分页数据显示、数据排序和过滤等功能! Google Tag Library 该标记库和 Google 有关。使用该标记库,利用 Google 为你的网站提供网站查询,并且可以...

    java开源包11

    GWT Advanced Table 是一个基于 GWT 框架的网页表格组件,可实现分页数据显示、数据排序和过滤等功能! Google Tag Library 该标记库和 Google 有关。使用该标记库,利用 Google 为你的网站提供网站查询,并且可以...

Global site tag (gtag.js) - Google Analytics