浏览 3854 次
精华帖 (0) :: 良好帖 (0) :: 新手帖 (0) :: 隐藏帖 (0)
|
|
---|---|
作者 | 正文 |
发表时间:2007-07-03
源文件为prototype_helper.rb,其中包括RJS的update_page调用的JavaScriptGenerator类的定义: 1,link_to_remote是简单的异步url请求 def link_to_remote(name, options = {}, html_options = {}) link_to_function(name, remote_function(options), html_options) end def remote_function(options) javascript_options = options_for_ajax(options) update = '' if options[:update] && options[:update].is_a?(Hash) update = [] update << "success:'#{options[:update][:success]}'" if options[:update][:success] update << "failure:'#{options[:update][:failure]}'" if options[:update][:failure] update = '{' + update.join(',') + '}' elsif options[:update] update << "'#{options[:update]}'" end function = update.empty? ? "new Ajax.Request(" : "new Ajax.Updater(#{update}, " url_options = options[:url] url_options = url_options.merge(:escape => false) if url_options.is_a?(Hash) function << "'#{url_for(url_options)}'" function << ", #{javascript_options})" function = "#{options[:before]}; #{function}" if options[:before] function = "#{function}; #{options[:after]}" if options[:after] function = "if (#{options[:condition]}) { #{function}; }" if options[:condition] function = "if (confirm('#{escape_javascript(options[:confirm])}')) { #{function}; }" if options[:confirm] return function end link_to_remote调用remote_function组织Prototype方法调用,然后调用link_to_function生成html标签和绑定onclick事件 看看例子: <%= link_to_remote "Delete this post", :update => "posts", :url => { :action => "destroy", :id => post.id } %> <%= link_to_remote "Undo", :url => { :action => "undo", :n => word_counter }, :complete => "undoRequestCompleted(request)" %> 其中:update参数用来在Ajax异步调用完成后更新页面DOM元素,通常controller返回的结果为一个partial 2,periodically_call_remote用来做周期性url请求 def periodically_call_remote(options = {}) frequency = options[:frequency] || 10 code = "new PeriodicalExecuter(function() {#{remote_function(options)}}, #{frequency})" javascript_tag(code) end 它new一个PeriodicalExecuter对象来完成周期性的工作,而callbacks等参数和link_to_remote一样,其中:frequency默认是10秒,看看例子: <%= periodically_call_remote :update => 'time_div', :url => {:action => "time"}, :frequency => 1.0 %> 3,observe_form和observe_field用来监听form和field,如果内容改变,则触发function调用 def observe_form(form_id, options = {}) if options[:frequency] build_observer('Form.Observer', form_id, options) else build_observer('Form.EventObserver', form_id, options) end end def observe_field(field_id, options = {}) if options[:frequency] && options[:frequency] > 0 build_observer('Form.Element.Observer', field_id, options) else build_observer('Form.Element.EventObserver', field_id, options) end end protected def build_observer(klass, name, options = {}) if options[:with] && !options[:with].include?("=") options[:with] = "'#{options[:with]}=' + value" else options[:with] ||= 'value' if options[:update] end callback = options[:function] || remote_function(options) javascript = "new #{klass}('#{name}', " javascript << "#{options[:frequency]}, " if options[:frequency] javascript << "function(element, value) {" javascript << "#{callback}}" javascript << ", '#{options[: on]}'" if options[: on] javascript << ")" javascript_tag(javascript) end build_observer方法给出了生成JavaScript的实现(这段代码看着有点reflection的意思),然后使用javascript_tag来将JavaScript写入模板 看看例子: <%= observe_form "entry-form", :frequency => 1, :update => "live-preview", :complete => "Element.show('live-preview')", :url => {:action => "preview"} %> <%= observe_field 'search', :frequency => 0.5, :update => 'target_id', :url => { :controller => '<controller>', :action=> 'list' }, :with => "'search=' + escape(value)" %> 4,JavaScriptGenerator def update_page(&block) JavaScriptGenerator.new(@template, &block).to_s end class JavaScriptGenerator def initialize(context, &block) #:nodoc: @context, @lines = context, [] include_helpers_from_context @context.instance_exec(self, &block) end private def include_helpers_from_context @context.extended_by.each do |mod| extend mod unless mod.name =~ /^ActionView::Helpers/ end extend GeneratorMethods end module GeneratorMethods def to_s returning javascript = @lines * $/ do if ActionView::Base.debug_rjs source = javascript.dup javascript.replace "try {\n#{source}\n} catch (e) " javascript << "{ alert('RJS error:\\n\\n' + e.toString()); alert('#{source.gsub('\\','\0\0').gsub(/\r\n|\n|\r/, "\\n").gsub(/["']/) { |m| "\\#{m}" }}'); throw e }" end end end def [](id) JavaScriptElementProxy.new(self, id) end def select(pattern) JavaScriptElementCollectionProxy.new(self, pattern) end def insert_html(position, id, *options_for_render) insertion = position.to_s.camelize call "new Insertion.#{insertion}", id, render(*options_for_render) end def replace_html(id, *options_for_render) call 'Element.update', id, render(*options_for_render) end def replace(id, *options_for_render) call 'Element.replace', id, render(*options_for_render) end def remove(*ids) loop_on_multiple_args 'Element.remove', ids end def show(*ids) loop_on_multiple_args 'Element.show', ids end def hide(*ids) loop_on_multiple_args 'Element.hide', ids end def toggle(*ids) loop_on_multiple_args 'Element.toggle', ids end def alert(message) call 'alert', message end def redirect_to(location) assign 'window.location.href', @context.url_for(location) end def call(function, *arguments, &block) record "#{function}(#{arguments_for_call(arguments, block)})" end def delay(seconds = 1) record "setTimeout(function() {\n\n" yield record "}, #{(seconds * 1000).to_i})" end def visual_effect(name, id = nil, options = {}) record @context.send(:visual_effect, name, id, options) end def sortable(id, options = {}) record @context.send(:sortable_element_js, id, options) end def draggable(id, options = {}) record @context.send(:draggable_element_js, id, options) end def drop_receiving(id, options = {}) record @context.send(:drop_receiving_element_js, id, options) end def call(function, *arguments, &block) record "#{function}(#{arguments_for_call(arguments, block)})" end private def page self end def record(line) returning line = "#{line.to_s.chomp.gsub(/\;\z/, '')};" do self << line end end def method_missing(method, *arguments) JavaScriptProxy.new(self, method.to_s.camelize) end end end 我们终于看到了update_page这个helper方法了,它初始化JavaScriptGenerator对象,然后调用to_s方法返回生成的JavaScript代码 而update_page后的block中page方法返回self,即返回JavaScriptGenerator对象 JavaScriptGenerator类中定义了select,replace,hide,delay,visual_effect等我们熟悉的helper方法 而其中visual_effect,sortable,draggable和drop_receiving实际上会调用scriptaculous_helper中相应的方法 看看RJS的例子: update_page do |page| page.insert_html :bottom, 'list', "<li>#{@item.name}</li>" page.visual_effect :highlight, 'list' page.hide 'status-indicator', 'cancel-link' end 而[]方法和select方法分别使用JavaScriptElementProxy和JavaScriptElementCollectionProxy代理类 如果大家想了解和熟悉RJS语法,多看看javascript_helper.rb,prototype_helper.rb,scriptaculous_helper.rb 声明:ITeye文章版权属于作者,受法律保护。没有作者书面许可不得转载。
推荐链接
|
|
返回顶楼 | |