免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 3631 | 回复: 0
打印 上一主题 下一主题

在Rails3时代js该怎么写? [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2010-12-29 10:49 |只看该作者 |倒序浏览
本帖最后由 中关村村草 于 2010-12-29 11:08 编辑

[前注:本篇不是教程,只是一些rails的新技巧、特性的探讨。]

Why?
现在,我们在进行软件、WEB项目开发时都用喜欢用框架,即省时省力,又有规有矩。所谓规矩,最常见的约束就是MVC三层分离,其中V是VIEW(视图),而进行WEB开发时,最常见的VIEW就是HTML页面。HTML到了XHTML(http://en.wikipedia.org/wiki/XHTML)时代,也开始强调了要样式与内容结构分离,“HCJ”三层分离,就是HTML(页面内容)、CSS(页面装饰)、JAVASCRIPT(页面行为)尘归尘土归土,各自归纳到独立的文件中,通过HTML的标签或属性来进行关联,最显而易见的好处是一来方便代码结构化管理、解析,二来方便浏览器缓存。

我们很幸运的,搭上了Rails这俩快车,一路走在流行技术的最前沿,在Rails3时代,Rails秉承“兼容并包”的良好品德和思想,在提供方便默认配置之余,还放开了怀抱,使得更多更方便的框架、类库可以集成进来替换默认配置。在Rails3中,Prototype这个js框架将不会默认绑定(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的纯洁。为了要维护HTML的纯洁以及JS的主权独立,于是计算机之神说,要Unobtrusive,于是有了Unobtrusive_JavaScript(http://en.wikipedia.org/wiki/Unobtrusive_JavaScript)。

Unobtrusive_JavaScript简单来说,就是把有指定元素行为的JS代码分离出来,如事件描述:
分离前:
Html代码
  1. <body>  
  2. <input type="text" name="date" onchange="validateDate(this);" />  
  3. </body>  
复制代码
分离后:
Html代码
  1. <head>
  2. <script>
  3. window.onload = function(){ //Wait for the page to load.
  4.     var inputs = document.getElementsByTagName('input');
  5.     for(var i=0,l=inputs.length;i<l;i++){
  6.         input = inputs[i];
  7.         if(input.name && input.name=='date'){
  8.             input.onchange = function(){
  9.                 validateDate(this);
  10.             }
  11.         }
  12.     }
  13. };

  14. function validateDate(){
  15.         //Do something when the content of the 'input' element with the name 'date' is changed.
  16. }
  17. </script>
  18. </head>
  19. <body>
  20. <input type="text" name="date" />
  21. </body>
复制代码
把所有js绑定操作都统一放到页面加载完再进行。

How?
用一个比较常见的用户信息修改操作为例,修改用户的信息,一般会用如下代码:
Ruby代码
  1. <% form_for @user do |f| %>
  2.   <%= f.error_messages %>
  3.   <p>
  4.     <%= f.label :username %><br />
  5.     <%= f.text_field :username %>
  6.   </p>
  7.   <p><%= f.submit "Submit" %></p>
  8. <% end %>
复制代码
会生成如下HTML代码:
Html代码
  1. <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>
  2.   <p>
  3.     <label for="user_username">Username</label><br />
  4.     <input id="user_username" name="user[username]" size="30" type="text" value="rainchen" />
  5.   </p>
  6.   <p><input id="user_submit" name="commit" type="submit" value="Submit" /></p>
  7. </form>
复制代码
如果要改为AJAX提交操作时,可以用remote_form_for helper(http://api.rubyonrails.org/class ... Helper.html#M001649),但这个其实是PrototypeHelper提供的一个辅助方法,把 form_for替换为remote_ form_for 后会在页面中生成如下HTML代码:
Html代码
  1. <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;">  
复制代码
经过之前的一番论述,现在的结论就是<form>标签不纯洁了,被插了一段Prototype的AJAX代码。

在Rails3时代,这种代码是不和谐的,需要批判。

在解决这个问题前,先看一下在Rails3时代,类似AJAX请求场景,是怎样实现的,如
Ruby代码
  1. <%= link_to 'Users', users_path %>  
复制代码
会生成HTML:
Html代码
  1. <a href="/users">Users</a>  
复制代码
用 remote_link_to 替换的话,将会得到:
Html代码
  1. <a href="/users" data-remote="true">Users</a>  

复制代码
被加进了一个data-remote属性,data-xxx 形式的属性,在HTML5中是合理又合法的:http://ejohn.org/blog/html-5-data-attributes/

remote_link_to 其实是一个Rails3新的AjaxHelper的方法,实现代码见:
http://github.com/rails/rails/bl ... pers/ajax_helper.rb

浏览代码后,不难发现到今天为止,AjaxHelper 中还没发现remote_form_for 的身影,也就是remote_form_for 还只是个传说。

今天我们就是要尝试实现这个传说,让我们就来见证奇迹。

在Rails3时代,没有意外的话,<form>标签也会被插入 data-remote=“true" 这个标记,因此思路很简单,覆盖掉remote_form_for 方法,加入这个标记,然后在页面加载后,用js查找带有这个标记的form,绑上AJAX操作即可。

1. 在application_helper.rb 中加入:
Ruby代码
  1.   # unobtrusive javascript helpers
  2.   def remote_form_for(*args, &proc)
  3.       options = args.extract_options!
  4.       options[:html] ||= {}
  5.       options[:html]["data-remote"] = "true"
  6.       options[:html]["data-update"] = options[:update] unless options[:update].blank? # enable update the resposne data to *update* dom
  7.       args << options
  8.       form_for(*args, &proc)
  9.   end
复制代码
2. 在js框架方面,我选择用jquery
在layout中加入
Ruby代码
  1. <%= javascript_include_tag respond_to?(:local_request?) && local_request? ? 'jquery.min' : 'http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js' %>
  2.     <%= 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' %>
  3.     <%= javascript_tag "AUTHENTICITY_TOKEN = '#{protect_against_forgery? ? form_authenticity_token : ""}';" %>
  4.     <%= javascript_include_tag 'application' %>
复制代码
在application.js 中加入:
Js代码
  1. $(function(){
  2.   // set authenticity koen for Rails
  3.   if(typeof AUTHENTICITY_TOKEN != 'undefined' && AUTHENTICITY_TOKEN != ''){
  4.     $.ajaxSetup( {data: {authenticity_token: AUTHENTICITY_TOKEN}} );
  5.   }

  6.   // setup app namespace
  7.   var app = {};

  8.   // show the ajax result
  9.    app.ajaxShowResult = function(options){
  10.     options = $.extend({title: '', body: '', width: 200, height: 200}, options);
  11.     if(!$("#app_ajax_result").get(0)){
  12.       $(document.body).append('<div id="app_ajax_result"></div>');
  13.       $("#app_ajax_result").dialog({
  14.         title: '',
  15.         bgiframe: true,
  16.         width: options.width,
  17.         height: options.height,
  18.         modal: true
  19.       });
  20.     }
  21.     $("#app_ajax_result").html(options.body);
  22.     $("#app_ajax_result").dialog('option', 'title', options.title);
  23.     return $("#app_ajax_result").dialog('open');
  24.    };

  25.   // default error handler for ajax request
  26.   app.ajaxError = function(XMLHttpRequest, textStatus, errorThrown){
  27.     return app.ajaxShowResult({title: XMLHttpRequest.statusText, body: XMLHttpRequest.responseText, width: 600, height: 400});
  28.   };

  29.   // default success handler for ajax request
  30.   app.ajaxSuccess = function(data, textStatus){
  31.     if(this.update){
  32.       $("#"+this.update).html(data);
  33.     }else if(this.dataType == 'html'){
  34.       return app.ajaxShowResult({title:textStatus, body: data});
  35.     }
  36.   };

  37.   app.ajax = function(options) {
  38.     $.ajax($.extend({ url : options.url, type : 'get', dataType: 'html', error: app.ajaxError, success: app.ajaxSuccess }, options));
  39.     return false;
  40.   };

  41.   // find all all data-remote tags
  42.   app.setupAjaxHelpers = function(){
  43.     // remote links handler
  44.     $('a[data-remote=true]').live('click', function() {
  45.       return app.ajax({ url : this.href });
  46.     });

  47.     // remote forms handler
  48.     $('form[data-remote=true]').live('submit', function() {
  49.       return app.ajax({ url : this.action, type : this.method, data : $(this).serialize(), update: $(this).attr('data-update') });
  50.     });
  51.   };

  52.     // init
  53.   app.init = function(){
  54.     app.setupAjaxHelpers();
  55.   };

  56.   app.init();

  57. });
复制代码
关键代码其实只是
Js代码
  1.   // remote forms handler
  2.     $('form[data-remote=true]').live('submit', function() {
  3.       return app.ajax({ url : this.action, type : this.method, data : $(this).serialize(), update: $(this).attr('data-update') });
  4.     });
复制代码
默认用jquery-ui来做结果显示。

3.要支持ajax方式获取无layout的action rendered page时,还应该在application_controller.rb里加入:
  1. #  render empty layout for ajax request
  2.   layout proc { |controller| controller.request.xhr? ? nil : 'application' }
复制代码
后注:
1. 其中jquery的live事件还不是实现得很完整,对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标签中是约定用css做标记还是用属性做标记,筛选时会性能问题的差异,对我来说不是关注重点。

3. 在现在的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?
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP