浏览 4211 次
精华帖 (0) :: 良好帖 (2) :: 新手帖 (0) :: 隐藏帖 (0)
|
|
---|---|
作者 | 正文 |
发表时间:2009-03-01
最后修改:2009-03-05
写在前头
绿色的部分是,这个介绍容易出问题部分的解释。本来就是面向零基础,才写的,所以不怕麻烦和琐碎。然而,如果你还是嫌太麻烦,或者你不希望了解细节的话。那么,我建议你直接点击这里,下载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 : <%= 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享用你的劳动成果。 如果,这个文档对你有那么半点帮助,我的时间就值得欣慰了。我再重复一下,有什么问题请在我的博客反馈。 谢谢 声明:ITeye文章版权属于作者,受法律保护。没有作者书面许可不得转载。
推荐链接
|
|
返回顶楼 | |