- 論壇徽章:
- 0
|
本帖最后由 中關(guān)村村草 于 2010-12-29 11:08 編輯
[前注:本篇不是教程,只是一些rails的新技巧、特性的探討。]
Why?
現(xiàn)在,我們在進(jìn)行軟件、WEB項目開發(fā)時都用喜歡用框架,即省時省力,又有規(guī)有矩。所謂規(guī)矩,最常見的約束就是MVC三層分離,其中V是VIEW(視圖),而進(jìn)行WEB開發(fā)時,最常見的VIEW就是HTML頁面。HTML到了XHTML(http://en.wikipedia.org/wiki/XHTML)時代,也開始強(qiáng)調(diào)了要樣式與內(nèi)容結(jié)構(gòu)分離,“HCJ”三層分離,就是HTML(頁面內(nèi)容)、CSS(頁面裝飾)、JAVASCRIPT(頁面行為)塵歸塵土歸土,各自歸納到獨立的文件中,通過HTML的標(biāo)簽或?qū)傩詠磉M(jìn)行關(guān)聯(lián),最顯而易見的好處是一來方便代碼結(jié)構(gòu)化管理、解析,二來方便瀏覽器緩存。
我們很幸運(yùn)的,搭上了Rails這倆快車,一路走在流行技術(shù)的最前沿,在Rails3時代,Rails秉承“兼容并包”的良好品德和思想,在提供方便默認(rèn)配置之余,還放開了懷抱,使得更多更方便的框架、類庫可以集成進(jìn)來替換默認(rèn)配置。在Rails3中,Prototype這個js框架將不會默認(rèn)綁定(http://rails.uservoice.com/pages ... -unit-and-prototype)。
What?
在Rails3時代還沒到臨之前,如果不想用Prototype,可以有jRails(http://ennerchi.com/projects/jrails)代替,然后jRails是完全兼容PrototypeHelper(http://api.rubyonrails.org/class ... rototypeHelper.html),同樣會在HTML中嵌入JS代碼片段,從而傷害了HTML的純潔。為了要維護(hù)HTML的純潔以及JS的主權(quán)獨立,于是計算機(jī)之神說,要Unobtrusive,于是有了Unobtrusive_JavaScript(http://en.wikipedia.org/wiki/Unobtrusive_JavaScript)。
Unobtrusive_JavaScript簡單來說,就是把有指定元素行為的JS代碼分離出來,如事件描述:
分離前:
Html代碼- <body>
- <input type="text" name="date" onchange="validateDate(this);" />
- </body>
復(fù)制代碼 分離后:
Html代碼- <head>
- <script>
- window.onload = function(){ //Wait for the page to load.
- var inputs = document.getElementsByTagName('input');
- for(var i=0,l=inputs.length;i<l;i++){
- input = inputs[i];
- if(input.name && input.name=='date'){
- input.onchange = function(){
- validateDate(this);
- }
- }
- }
- };
-
- function validateDate(){
- //Do something when the content of the 'input' element with the name 'date' is changed.
- }
- </script>
- </head>
- <body>
- <input type="text" name="date" />
- </body>
復(fù)制代碼 把所有js綁定操作都統(tǒng)一放到頁面加載完再進(jìn)行。
How?
用一個比較常見的用戶信息修改操作為例,修改用戶的信息,一般會用如下代碼:
Ruby代碼- <% form_for @user do |f| %>
- <%= f.error_messages %>
- <p>
- <%= f.label :username %><br />
- <%= f.text_field :username %>
- </p>
- <p><%= f.submit "Submit" %></p>
- <% end %>
復(fù)制代碼 會生成如下HTML代碼:
Html代碼- <form action="/users/2" class="edit_user" id="edit_user_2" method="post"><div style="margin:0;padding:0"><input name="_method" type="hidden" value="put" /><input name="authenticity_token" type="hidden" value="ooDWeKPVumeI0r+O4E20g9TjfnxFKHp3ZsnCPCCrSFg=" /></div>
- <p>
- <label for="user_username">Username</label><br />
- <input id="user_username" name="user[username]" size="30" type="text" value="rainchen" />
- </p>
- <p><input id="user_submit" name="commit" type="submit" value="Submit" /></p>
- </form>
復(fù)制代碼 如果要改為AJAX提交操作時,可以用remote_form_for helper(http://api.rubyonrails.org/class ... Helper.html#M001649),但這個其實是PrototypeHelper提供的一個輔助方法,把 form_for替換為remote_ form_for 后會在頁面中生成如下HTML代碼:
Html代碼- <form action="/users/2" class="edit_user" id="edit_user_2" method="post" onsubmit="new Ajax.Request('/users/2', {asynchronous:true, evalScripts:true, parameters:Form.serialize(this)}); return false;">
復(fù)制代碼 經(jīng)過之前的一番論述,現(xiàn)在的結(jié)論就是<form>標(biāo)簽不純潔了,被插了一段Prototype的AJAX代碼。
在Rails3時代,這種代碼是不和諧的,需要批判。
在解決這個問題前,先看一下在Rails3時代,類似AJAX請求場景,是怎樣實現(xiàn)的,如
Ruby代碼- <%= link_to 'Users', users_path %>
復(fù)制代碼 會生成HTML:
Html代碼- <a href="/users">Users</a>
復(fù)制代碼 用 remote_link_to 替換的話,將會得到:
Html代碼- <a href="/users" data-remote="true">Users</a>
-
復(fù)制代碼 被加進(jìn)了一個data-remote屬性,data-xxx 形式的屬性,在HTML5中是合理又合法的:http://ejohn.org/blog/html-5-data-attributes/
remote_link_to 其實是一個Rails3新的AjaxHelper的方法,實現(xiàn)代碼見:
http://github.com/rails/rails/bl ... pers/ajax_helper.rb
瀏覽代碼后,不難發(fā)現(xiàn)到今天為止,AjaxHelper 中還沒發(fā)現(xiàn)remote_form_for 的身影,也就是remote_form_for 還只是個傳說。
今天我們就是要嘗試實現(xiàn)這個傳說,讓我們就來見證奇跡。
在Rails3時代,沒有意外的話,<form>標(biāo)簽也會被插入 data-remote=“true" 這個標(biāo)記,因此思路很簡單,覆蓋掉remote_form_for 方法,加入這個標(biāo)記,然后在頁面加載后,用js查找?guī)в羞@個標(biāo)記的form,綁上AJAX操作即可。
1. 在application_helper.rb 中加入:
Ruby代碼- # unobtrusive javascript helpers
- def remote_form_for(*args, &proc)
- options = args.extract_options!
- options[:html] ||= {}
- options[:html]["data-remote"] = "true"
- options[:html]["data-update"] = options[:update] unless options[:update].blank? # enable update the resposne data to *update* dom
- args << options
- form_for(*args, &proc)
- end
復(fù)制代碼 2. 在js框架方面,我選擇用jquery
在layout中加入
Ruby代碼- <%= javascript_include_tag respond_to?(:local_request?) && local_request? ? 'jquery.min' : 'http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js' %>
- <%= javascript_include_tag respond_to?(:local_request?) && local_request? ? 'jquery-ui.min' : 'http://ajax.googleapis.com/ajax/libs/jqueryui/1.7.2/jquery-ui.min.js' %>
- <%= javascript_tag "AUTHENTICITY_TOKEN = '#{protect_against_forgery? ? form_authenticity_token : ""}';" %>
- <%= javascript_include_tag 'application' %>
復(fù)制代碼 在application.js 中加入:
Js代碼- $(function(){
- // set authenticity koen for Rails
- if(typeof AUTHENTICITY_TOKEN != 'undefined' && AUTHENTICITY_TOKEN != ''){
- $.ajaxSetup( {data: {authenticity_token: AUTHENTICITY_TOKEN}} );
- }
- // setup app namespace
- var app = {};
- // show the ajax result
- app.ajaxShowResult = function(options){
- options = $.extend({title: '', body: '', width: 200, height: 200}, options);
- if(!$("#app_ajax_result").get(0)){
- $(document.body).append('<div id="app_ajax_result"></div>');
- $("#app_ajax_result").dialog({
- title: '',
- bgiframe: true,
- width: options.width,
- height: options.height,
- modal: true
- });
- }
- $("#app_ajax_result").html(options.body);
- $("#app_ajax_result").dialog('option', 'title', options.title);
- return $("#app_ajax_result").dialog('open');
- };
- // default error handler for ajax request
- app.ajaxError = function(XMLHttpRequest, textStatus, errorThrown){
- return app.ajaxShowResult({title: XMLHttpRequest.statusText, body: XMLHttpRequest.responseText, width: 600, height: 400});
- };
- // default success handler for ajax request
- app.ajaxSuccess = function(data, textStatus){
- if(this.update){
- $("#"+this.update).html(data);
- }else if(this.dataType == 'html'){
- return app.ajaxShowResult({title:textStatus, body: data});
- }
- };
- app.ajax = function(options) {
- $.ajax($.extend({ url : options.url, type : 'get', dataType: 'html', error: app.ajaxError, success: app.ajaxSuccess }, options));
- return false;
- };
- // find all all data-remote tags
- app.setupAjaxHelpers = function(){
- // remote links handler
- $('a[data-remote=true]').live('click', function() {
- return app.ajax({ url : this.href });
- });
- // remote forms handler
- $('form[data-remote=true]').live('submit', function() {
- return app.ajax({ url : this.action, type : this.method, data : $(this).serialize(), update: $(this).attr('data-update') });
- });
- };
- // init
- app.init = function(){
- app.setupAjaxHelpers();
- };
- app.init();
- });
復(fù)制代碼 關(guān)鍵代碼其實只是
Js代碼- // remote forms handler
- $('form[data-remote=true]').live('submit', function() {
- return app.ajax({ url : this.action, type : this.method, data : $(this).serialize(), update: $(this).attr('data-update') });
- });
復(fù)制代碼 默認(rèn)用jquery-ui來做結(jié)果顯示。
3.要支持ajax方式獲取無layout的action rendered page時,還應(yīng)該在application_controller.rb里加入:- # render empty layout for ajax request
- layout proc { |controller| controller.request.xhr? ? nil : 'application' }
復(fù)制代碼 后注:
1. 其中jquery的live事件還不是實現(xiàn)得很完整,對IE支持不好:
引用
jQuery is going to be adding support for .live("submit") in the next
release, and it's possible to emulate in rails.jquery.js in the interim.
-- Yehuda
On Tue, May 26, 2009 at 1:07 PM, meefs...@googlemail.com <
- Show quoted text -
--
Yehuda Katz
Developer | Engine Yard
(ph) 718.877.1325
2. 在HTML標(biāo)簽中是約定用css做標(biāo)記還是用屬性做標(biāo)記,篩選時會性能問題的差異,對我來說不是關(guān)注重點。
3. 在現(xiàn)在的Rails中使用Unobtrusive_JavaScript 版本的 remote_link_to 可參考:http://blog.solnic.eu/2009/09/08 ... -helpers-in-rails-3
參考:
http://blog.solnic.eu/2009/09/08 ... -helpers-in-rails-3
http://groups.google.com/group/r ... ad/3fa1cc2b1979a858
http://nutrun.com/weblog/unobtrusive-ajax-with-jquery-and-rails/
http://docs.jquery.com/Events/live
End? |
|