136: jQuery
------------------------------------------------------------------------------------------------------------------------------
翻译的过程中查询了一些资料,得知如果$.post()设置dataType为‘script',就可以返回纯文本 JavaScript 代码,不用再设置MIME信息。
第一次翻译,鸟语和技术水平有限,非逐字逐句翻译,只是想用自己的话把作者的意思表达出来。译文难免存有纰漏,还请多多指正。
------------------------------------------------------------------------------------------------------------------------------
第43集演示了通过ajax请求的响应更新页面上的多个元素,利用Propotype库和form_remote_for方法异步提交一个表单,然后RJS模板输出可以用于修改页面元素的JavaScrip作为响应。在这一集,我们将使用jQuery做类似的事情 。
既然我们使用jQuery,就不能使用RJS或者Rails提供的form_remote_for等方法了。如果我们仍然希望能使用RJS,一个叫JRails的插件利用jQuery可能重写了这些方法。但我们仍将使用jQuery,因为它提供非侵入式的JavaScript去增强我们的Rails应用程序。
我们将使用原来开发过的那个应用程序。下面的页面显示了一个产品并且允许顾客添加评论。当添加一条评论后,它被显示在评论列表,显示评论数量的数字被更新,表单被重置并且显示一条感谢顾客评论的
flash消息。
当添加一条评论后,当前程序提交表单并重新加载页面。现在我们将写一些jQuery代码,把表单数据通过一个Ajax请求提交,并且无刷新地更新页面。
给我们的应用添加jQuery
首先要做的就是给应用程序添加jQuery库, jQuery库能够从jQuery网站下站,下载后放在public/javascripts目录下面。其次,我们在应用程序layout页面的<head>区添加如下的代码来引用它。
<%= javascript_include_tag ['jquery-1.3.2', 'application'] %>
jQuery每次都发行2个版本:一个适合开发环境,另一个是经过压缩的,适合生产环境。当你的网站还在开发阶段时,你最好使用开发版本,因为这样调试jQuery代码更方便。 和引用jQuery文件一样,我们还要引用applicaton.js文件, 我们将在它里面写jQuery代码。需要注意的是,jQuery文件必须最先添加以便它能够在其他JavaScript文件之前加载。
添加非侵入式的JavaScript
添加非侵入式的JavaScritp,意味着我们不修改视图文件中的代码。事实上,我们将使用jQuery动态地更改页面行为。 我们将从编写一个表单提交时会运行的函数开始。
表单的id为''new_review”,下面的代码将通过一个AJAX的方式提交这个表单。
$(document).ready(function (){ $('#new_review').submit(function (){ $.post($(this).attr('action'), $(this).serialize(), null, "script"); return false; }); });
代码的第一行创建了一个函数,这个函数可以在页面的DOM加载后运行。第二行通过id查找表单并创建一个当表单被提交时运行的函数。第三行创建一个提交到服务器的AJAX请求。$.post函数需要4个参数。第一个为提交的URL,也就是表单的action属性值。第二个参数是我们要传输的数据,在这里我们使用jQuery的serialize方法,它将很方便的把将要发送到服务器的表单元素序列化成一系列的键值对。第三个参数是一个回调函数, 当AJAX请求完成后,它将被执行,但是我们不使用它,它将被设置为null。 之所以没有使用回调函数,是因为我们设置第4个参数为”scirpt”, 它提示函数:服务端将返回浏览器可执行的JavaScript代码。 更多关于$.post的细节可以访问jQuery文档。最后我们设置返回false以保证默认的提交动作不会被触发。
更新Control
有了通过AJAX提交的表单,我们将修改评论控制器以保证它能处理HTML和JavaScript请求。 目前它保存新评论,重定向到产品页面。 在 JavaScript的请求环境中,一个重定向是毫无意义的,所以页面必须返回不同的响应。我们使用respond_to去设置2个不同的返回块。
def create @review = Review.create!(params[:review]) flash[:notice] = "Thanks for your review!" respond_to do |format| format.html { redirect_to @review.product } format.js end end
js块是空的,因为它需要做的只是返回我们创建的JavaScript代码。这些代码放在app/views/reviews
目录下面的craete.js.erb中。在没有JRails的时候我们不能通过jQuery使用RJS,但是我们使用js.erb
替代。为了测试代码是否正常工作我们将添加一个简单的alert函数到create.js.erb文件。
alert('Hello from AJAX');
在测试之前,还有一些很重要的事情要做。目前这些代码将不能正常运行,因为Rails不知道如何格式化jQuery发送的请求, 默认的按照HTML去处理。因此我们将告诉Rails:我们发送的是一个JavaScript请求。
有2种方法去做这件事。我们可以给AJAX请求的URL添加一个.js扩展名,或者使用jQuery的ajaxSetup方法设置请求报头保证它只接收JavaScript。第二种方法的优点是只需要设置一次就可以对所有的AJAX请求都有效。通常我们把下面的代码添加在application.js的顶部。
jQuery.ajaxSetup({ 'beforeSend': function (xhr) {xhr.setRequestHeader("Accept", "text/javascript")} });
添加报头的目的是为了告诉Rails调用js块,返回js.erb文件的内容。现在我们刷新页面
更新页面
弹出框出现了,所以我们的create.js.erb文件被返回并执行了。现在我们可以写一些jQuery代码替代弹出框,这些代码能将更新页面当添加新的评论时。为此,我们需要做四件事。
首先,我们将在页面添加flash消息感谢用户添加评论,我们用在表单前添加一个div的方式实现。
$('#new_review').before('<div id="flash_notice"><%= escape_javascript(flash.delete(:notice)) %></div>');
为了添加动态内容, 我们在JavaScript中嵌入erb标签。我们使用escape_javascript保证从erb中输出安全的JavaScript。请注意,我们马上调用flash.delete方法清除flash消息保证它不会出现在下次请求中。
用来显示评论数量的页面元素也应该被更新,它的id叫做reviews_count。jQuery的html方法可以用来更新它的内容。
$('#reviews_count').html('<%= pluralize(@review.product.reviews.count, "Review") %>');
接下来我们将在评论列表里添加一条新评论。评论是根据id有序显示的。每一条评论都被当作一个列表项通过一个paritial渲染。我们可以通过partial渲染一条新评论并且把它添加到评论列表里。
$('#reviews').append("<%= escape_javascript(render(:partial => @review)) %>");
最后我们重置表单。
$('#new_review')[0].reset();
jQuery的$返回一个jQuery对象。我们可以用数据的第一个元素代表实际的Dom元素,然后通过它调用reset()方法清空表单。
jQuery扩展
接下来我们将告诉你如何方便的添加自定义的jQuery函数。目前当表单提交时,我们运行着这样的jQuery
代码。
$('#new_review').submit(function (){ $.post($(this).attr('action'), $(this).serialize(), null, "script"); return false; });
如果希望网站上的多个表单都能够这样做,那么我们可以把这段代码抽取到一个新jQuery函数里,因此我们可以调用
$('#new_review').submitWithAjax();
jQuery很容易扩展自定义,我们这样定义新函数
jQuery.fn.submitWithAjax = function () { this.submit(function () { $.post($(this).attr('action'), $(this).serialize(), null, "script"); return false; }); };
上面就是我们要做的,现在我们就可以通过调用submitWithAjax异步提交表单。