如果沒有聽說過 Rails,那么歡迎您外星旅行歸來,近幾年大概只有那個地方?jīng)]有聽說過 Ruby on Rails 了。Rails 最吸引人的地方是能夠很快地建立功能完備的應(yīng)用程序并運行起來。Rails 為 Ajax 而內(nèi)置集成的 Prototype.js 庫可以輕松快速地創(chuàng)建所謂的富 Internet 應(yīng)用程序。
本文將逐步引導(dǎo)您創(chuàng)建 Rails 應(yīng)用程序。然后深入分析如何利用 Ajax 特性編寫從服務(wù)器上讀寫數(shù)據(jù)的 JavaScript 代碼。
從容起步 Ajax 之旅——Ajax 技術(shù)資源中心
什么是 Ajax?Ajax(Asynchronous JavaScript and XML)是一種編程技術(shù),它允許結(jié)合 XML 和 JavaScript 用于 Web 應(yīng)用程序,從而打破頁面刷新的范式,使您的用戶快速方便的與 Web 應(yīng)用程序交互。
您想了解如何構(gòu)建基于 Ajax 的應(yīng)用嗎?developerWorks 中國網(wǎng)站上有非常多的關(guān)于 Ajax 的文章、教程和技巧,通過“Ajax 技術(shù)資源中心”,您可以非??旖莸卣业侥軒椭瓿膳c Ajax 相關(guān)的應(yīng)用開發(fā)的技術(shù)參考資源。
關(guān)于 Rails 的一點說明
那么到底什么是 Rails 呢?Rails 是建立在 Ruby 編程語言上的一種 Web 應(yīng)用程序平臺。Ruby 存在大約有 10 年了。和 Perl 以及 Python 一樣,它也是一種開放源碼的敏捷編程語言,完全支持面向?qū)ο缶幊獭?/p>
Rails 作為一種應(yīng)用程序框架,強調(diào)使用正確的 Web 應(yīng)用程序模式,即模型-視圖-控制器(MVC)。在這里,系統(tǒng)的模型部分通常用一組映射到數(shù)據(jù)庫表的 ActiveRecord 對象表示??刂破鞑糠质且粋€ Ruby 類,其方法可以對模型執(zhí)行各種操作。視圖一般就是通過 ERB 模板(ERB 是 Ruby 內(nèi)置的文本模板包)生成的超文本標記語言代碼(HTML),形式上和 PHP 或 JavaServer Pages(JSP)代碼生成的 HTML 類似。視圖也可以是可擴展標記語言(XML)、文本、JavaScript 代碼、圖片或者其他東西。
用戶從 Rails Web 應(yīng)用程序請求頁面時,URL 通過路由系統(tǒng)發(fā)送,后者將請求發(fā)送給控制器??刂破鲝哪P驼埱髷?shù)據(jù)并發(fā)送給視圖完成格式化。
創(chuàng)建 Rails 應(yīng)用程序時,系統(tǒng)自動生成一些目錄和基本文件。包括隨系統(tǒng)安裝的 JavaScript 文件目錄(包括 Prototype.js 庫)、視圖、模型和控制器目錄,甚至還有存放從其他開發(fā)人員那里下載的插件的目錄。
開始使用 Rails
創(chuàng)建 Rails 應(yīng)用程序最簡單的辦法就是使用一個預(yù)先打好包的 Rails 系統(tǒng)。如果平臺是 Microsoft? Windows?,建議您使用 Instant Rails。在 Macintosh 機器上我非常喜歡 Locomotive 2 應(yīng)用程序。這些應(yīng)用程序都包括 Rails 框架、Ruby 語言、Web 服務(wù)器和 MySQL。下載了這么多東西(確實是這樣)之后,創(chuàng)建 Rails 應(yīng)用程序僅僅是舉手之勞了。
本文將創(chuàng)建一個新的菜譜應(yīng)用程序,稱為Recipe,只需要一個表。清單 1 顯示了 Recipe 應(yīng)用程序的數(shù)據(jù)庫遷移。
清單 1. 數(shù)據(jù)庫遷移
class CreateRecipes ActiveRecord::Migration
def self.up
create_table ( :recipes, :options => 'TYPE=InnoDB' ) do |t|
t.column :name, :string, :null => false
t.column :description, :text, :null => false
t.column :ingredients, :text, :null => false
t.column :instructions, :text, :null => false
end
end
def self.down
drop_table :recipes
end
end
數(shù)據(jù)庫中只有一個表:recipes。包含五個字段:name、description、ingredients、instructions,還有一個字段是 Rails 基礎(chǔ)設(shè)施自動維護的唯一標識符。
建立好數(shù)據(jù)庫表之后,接下來就要為它包裝一個 ActiveRecord 對象。該對象如 清單 2 所示。
清單 2. Recipe 模型
class Recipe ActiveRecord::Base
validates_presence_of :name
validates_presence_of :description
validates_presence_of :ingredients
validates_presence_of :instructions
end
ActiveRecord 基類負責(zé)所有基本的數(shù)據(jù)庫訪問:查詢表、插入、更新和刪除記錄。這里只要為每個字段增加驗證就夠了。我告訴 Rails 每個字段不能為空。
Ajax 表單
創(chuàng)建 Recipe 應(yīng)用程序的第一步是向數(shù)據(jù)庫中添加菜譜。首先介紹在 Rails 中創(chuàng)建基本 HTML 表單的標準方法。如 清單 3 中的 RecipesController 類所示。
清單 3. Recipes_controller.rb
class RecipesController ApplicationController
def add
@recipe = Recipe.new
if request.post?
@recipe.name = params[:recipe][:name]
@recipe.description = params[:recipe][:description]
@recipe.ingredients = params[:recipe][:ingredients]
@recipe.instructions = params[:recipe][:instructions]
@recipe.save
end
end
end
只有一個方法 add,它首先創(chuàng)建一個空的 Recipe 對象。然后當客戶機發(fā)出請求時添加參數(shù)并保存數(shù)據(jù)。
該頁面的 ERB 模板如 清單 4 所示。
清單 4. Add.rhml
html>
body>
%= error_messages_for 'recipe' %>br/>
%= start_form_tag %>
table>
tr>td>Name/td>
td>%= text_field :recipe, :name %>/td>/tr>
tr>td>Description/td>
td>%= text_area :recipe, :description, :rows => 3 %>/td>/tr>
tr>td>Ingredients/td>
td>%= text_area :recipe, :ingredients, :rows => 3 %>/td>/tr>
tr>td>Instructions/td>
td>%= text_area :recipe, :instructions, :rows => 3 %>/td>/tr>
/table>
%= submit_tag 'Add' %>
%= end_form_tag %>
/body>
/html>
頁面首先顯示 recipe 對象的錯誤消息。如果用戶發(fā)布的數(shù)據(jù)沒有通過 Recipe 模型對象的驗證就會設(shè)置這些消息。然后依次為 form> 標記、每個字段的 text_field 和 text_area 項、submit> 標記和表單末尾。
這是非常標準的 Rails。安全可靠,可用于各種瀏覽器,清晰地映射到為客戶機創(chuàng)建的 HTML。但我需要的是 Web 2.0,就是說 Ajax。那么,該如何修改呢?
控制器端的 add() 方法徹底變了,如 清單 5 所示。
清單 5. Recipes_controller.rb
class RecipesController ApplicationController
def add
end
def add_ajax
Recipe.create( { :name => params[:recipe][:name],
:description => params[:recipe][:description],
:ingredients => params[:recipe][:ingredients],
:instructions => params[:recipe][:instructions] } )
end
end
add() 方法不再做任何事,因為有一個新的方法 add_ajax() 處理客戶機返回的數(shù)據(jù)。
對于模板來說,不需要做大的修改,如 清單 6 所示。
清單 6. add.rhtml 的開始部分
html>
head>
%= javascript_include_tag :defaults %>
/head>
body>
div id="counter">/div>
%= form_remote_tag :url => { :action => 'add_ajax' },
:complete => 'document.forms[0].reset();',
:update => 'counter' %>
table>
tr>td>Name/td>
文件開始在 HTML 中增加了 head 部分,其中包含對 Rails 默認 JavaScript 文件的引用。這就是 Prototype.js 系統(tǒng),完成 Ajax 工作的大部分。
然后添加了 div> 標記 counter,保存從 Ajax 請求返回的結(jié)果。并不是非常必要,但給用戶一些反饋也不錯。
最后,將原來的 start_form_tag 改為 form_remote_tag。form_remote_tag 有幾個參數(shù),其中最重要的是 url,它指定向何處發(fā)送數(shù)據(jù)。第二個是一個完整的處理程序,其中的 JavaScript 代碼在 Ajax 請求完成時執(zhí)行。這里重置表單以便讓用戶輸入新的菜譜。然后使用 update 參數(shù)告訴 Rails 把 add_ajax 動作的輸出發(fā)送到何處。
add_ajax() 方法還需要一個模板。如 清單 7 所示。
清單 7. Add_ajax.rhtml
%= Recipe.find(:all).length %> recipes now in database
如此而已。但并不完全是。這僅僅是從標準 HTML 表單遷移到 Rails 中的 Ajax 表單。圖 1 顯示了準備提交的 Ajax 表單。
圖 1. Ajax 表單
接下來是更加動態(tài)的交互,比如使用 Ajax 動態(tài)搜索。
Ajax 動態(tài)搜索
Prototype.js 提供了觀察頁面上的字段和表單的功能。我利用這種功能觀察一個文本字段,可在其中輸入部分菜譜名。然后運行 RecipesController 的搜索方法,輸出放在文本字段下方的 div> 標記中。首先從更新的 RecipesController 開始,如 清單 8 所示。
清單 8. Recipes_controller.rb
class RecipesController ApplicationController
...
def index
end
def search_ajax
@recipes = Recipe.find( :all,
:conditions => [ "name LIKE ?",
"%#{params[:recipe][:name]}%" ] )
render :layout=>false
end
end
index() 方法呈現(xiàn) HTML 表單。search_ajax() 方法根據(jù)搜索參數(shù)查找菜譜并把數(shù)據(jù)發(fā)送給 ERB 模板格式化。index.rtml 模板如 清單 9 所示。
清單 9. Index.rhtml
html>
head>
%= javascript_include_tag :defaults %>
/head>
body>
%= form_tag nil, { :id => 'search_form' } %>
%= text_field 'recipe', 'name' %>
%= end_form_tag %>
div id="recipe">
/div>
%= observe_form :search_form, :frequency => 0.5,
:update => 'recipe',
:url => { :action => 'search_ajax' } %>
/body>
/html>
在 清單 9 的開頭同樣包括了 JavaScript 庫。然后創(chuàng)建一個具有搜索字段和 div> 標記的 form 表單用來保存搜索返回的數(shù)據(jù)。最后調(diào)用 observe_form() 輔助方法,它創(chuàng)建 JavaScript 代碼觀察表單的變化,并把表單數(shù)據(jù)發(fā)送到 search_ajax() 方法。然后將該方法的結(jié)果放到 recipe div> 中。
search_ajax.rhtml 表單的代碼如 清單 10 所示。
清單 10. Search_ajax.rhtml
% @recipes.each { |r| %>
h1>%= r.name %>/h1>
p>%= r.description %>/p>
% } %>
由于搜索結(jié)果可能有多項,循環(huán)遍歷菜譜列表并輸出其名稱和說明。
當在瀏覽器中打開該站點并在地址欄中輸入 apple 的時候,我找到了蘋果餡餅的做法,如 圖 2 所示。
圖 2. Ajax 動態(tài)搜索
這就是最簡單的實現(xiàn)。但如果希望進一步了解蘋果餡餅該如何做?可以使用 Ajax 在需要時動態(tài)獲得配料和做法嗎?很高興您這樣問!當然能!
在命令中增加內(nèi)容
有時候,讓用戶選擇下載更多的信息而不是生硬地把信息堆到頁面上更加友好。傳統(tǒng)上,Web 應(yīng)用程序開發(fā)人員使用隱藏的 div> 標記包含信息,當用戶要求這些資料時再顯示出來。Rails 提供了一種更優(yōu)美的方法,可以在需要的時候使用 Ajax 請求數(shù)據(jù)。
清單 11 中的菜單模板增加了 link_to_remote() 輔助方法調(diào)用。
清單 11. Search_ajax.rhtml
% @recipes.each { |r| %>
h1>%= r.name %>/h1>
p>%= r.description %>/p>
div id="extra_%= r.id %>">/div>
%= link_to_remote 'Extra',
:url => { :action => 'get_extra_ajax', :id => r.id },
:update => "extra_#{r.id}" %>
% } %>
link_to_remote() 在頁面中添加 JavaScript 代碼和包含指定文本的 anchor(a>)標記??蛻魡螕粼撴溄訒r,頁面就會發(fā)出 Ajax 請求來獲得新內(nèi)容并替代原來的文本。
為了獲得新的信息,必須為 RecipesController 添加一個 get_extra_ajax() 方法。如 清單 12 所示。
清單 12. Recipes_controller.rb
class RecipesController ApplicationController
...
def get_extra_ajax
@recipe = Recipe.find( params[:id] )
render :layout=>false
end
end
此外還需要一個模板 get_extra_ajax.rhtml 來格式化這些信息。清單 13 顯示了該模板。
清單 13. Get_extra_ajax.rhtml
blockquote>%= simple_format @recipe.ingredients %>/blockquote>
p>%= simple_format @recipe.instructions %>/p>
現(xiàn)在回到頁面并輸入 apple,將看到 圖 3 所示的結(jié)果。
圖 3. 增加了可以訪問配料及做法的鏈接
單擊該鏈接時,瀏覽器使用 Ajax 從 Web 服務(wù)器檢索額外的資料并顯示在那個位置。結(jié)果如 圖 4 所示。
圖 4. 菜譜的細節(jié)
如果有一個行項或細節(jié)類型的報告,這種 Ajax 模式非常方便,因為請求每個記錄的細節(jié)可能非常耗時,最好在需要的時候再請求。此外這種技術(shù)也有助于節(jié)約屏幕資源。
可能最熱門的 Web 2.0 特性是自動完成文本字段。否則 這趟 Ajax 之旅怎么能算完整呢?
自動完成字段
Rails 使得構(gòu)建自動完成字段極其簡單。首先要在 index.rhtml 模板中增加一些東西。修改后的版本如 清單 14 所示。
清單 14. 修改后的 index.rhtml
html>
head>
%= javascript_include_tag :defaults %>
style>
div.auto_complete {
width: 300px;
background: #fff;
}
div.auto_complete ul {
border: 1px solid #888;
margin: 0px;
padding: 0px;
width: 100%;
list-style-type: none;
}
div.auto_complete ul li {
margin: 0px;
padding: 3px;
}
div.auto_complete ul li.selected {
background-color: #ffb;
}
div.auto_complete ul strong.highlight {
color: #800;
margin: 0px;
padding: 0px;
}
/style>
/head>
body>
%= form_tag nil, { :id => 'search_form' } %>
p>%= text_field 'recipe', 'name', :autocomplete => 'off' %>/p>
div class="auto_complete" id="recipe_name_auto_complete">/div>
%= auto_complete_field :recipe_name,
:url => { :action=>'autocomplete_recipe_name' },
:tokens => ',' %>
%= end_form_tag %>
...
文件上方的級聯(lián)樣式表(CSS)部分用于自動完成字段的下拉列表。此外還對 text_field 略做修改關(guān)閉瀏覽器的自動完成機制。下拉列表中的項放在新增的 div> 中,這些項將調(diào)用 auto_complete() 方法。auto_complete() 輔助方法創(chuàng)建客戶端 JavaScript 代碼調(diào)用服務(wù)器上的 autocomplete_recipe_name() 方法以及 recipe name 文本字段的當前內(nèi)容。
RecipesController 的 autocomplete_recipe_name() 方法搜索該名稱,如 清單 15 所示。
清單 15. Recipes_controller.rb
class RecipesController ApplicationController
...
def autocomplete_recipe_name
@recipes = Recipe.find( :all,
:conditions => [ "name LIKE ?",
"%#{params[:recipe][:name]}%" ] )
render :layout=>false
end
end
還需要一個 ERB 模板建立列表,如 清單 16 所示。
清單 16. Autocomplete_recipe_list.rb
ul class="autocomplete_list">
% @recipes.each { |r| %>
li class="autocomplete_item">%= r.name %>/li>
% } %>
/ul>
自動完成系統(tǒng)查找一個 HTML 列表(ul>),其中每個列表項都是一個 option。使用 index.rhtml 頁面的 CSS (或者您提供的樣式表)格式化。
為了查看自動完成的效果,在瀏覽器中打開頁面,然后輸入 test。我在 test 菜譜中加入了一些數(shù)據(jù)。結(jié)果如 圖 5 所示。
圖 5. 下拉自動完成列表
可以使用上下箭頭鍵選擇一個選項然后按 Enter 鍵選擇。這樣將把選擇的內(nèi)容復(fù)制到文本字段中。
非常靈活,感謝 Rails 體系結(jié)構(gòu),它使這很容易實現(xiàn)。
結(jié)束語
無需諱言,我喜歡 Rails。從使用它的那一刻起我就被它深深吸引了。就我所見,Web 的很多開發(fā)人員都被它吸引了。為什么不呢?Rails 使得創(chuàng)建高交互性的 Web 應(yīng)用程序易如反掌。
即使您還沒有開始編寫 Rails 應(yīng)用程序,我也建議您下載 Instant Rails 或 Locomotive 應(yīng)用程序開始嘗試一下。您將體會到很多樂趣,并學(xué)習(xí)到很多可用于 Java? PHP 或 Microsoft .NET 應(yīng)用程序的東西。也許您會發(fā)現(xiàn)您希望一直編寫 Rails 代碼。
您可能感興趣的文章:- Windows下Ruby on Rails開發(fā)環(huán)境安裝配置圖文教程
- ruby on rails 代碼技巧
- 攻克CakePHP(PHP中的Ruby On Rails框架)圖文介紹
- 在阿里云 (aliyun) 服務(wù)器上搭建Ruby On Rails環(huán)境
- CentOS中配置Ruby on Rails環(huán)境
- win7安裝ruby on rails開發(fā)環(huán)境
- ruby on rails中Model的關(guān)聯(lián)詳解