diff options
-rw-r--r-- | actionpack/CHANGELOG | 9 | ||||
-rw-r--r-- | actionpack/lib/action_view/helpers/javascript_helper.rb | 78 | ||||
-rw-r--r-- | actionpack/test/controller/capture_test.rb | 39 | ||||
-rw-r--r-- | actionpack/test/fixtures/test/capturing.rhtml | 4 | ||||
-rw-r--r-- | actionpack/test/fixtures/test/update_element_with_capture.rhtml | 9 |
5 files changed, 139 insertions, 0 deletions
diff --git a/actionpack/CHANGELOG b/actionpack/CHANGELOG index 26c0af507f..174137ac30 100644 --- a/actionpack/CHANGELOG +++ b/actionpack/CHANGELOG @@ -1,5 +1,14 @@ *SVN* +* Added JavascriptHelper#update_element_function, which returns a Javascript function (or expression) that'll update a DOM element according to the options passed #933 [mortonda@dgrmm.net]. Examples: + + <%= update_element_function("products", :action => :insert, :position => :bottom, :content => "<p>New product!</p>") %> + + <% update_element_function("products", :action => :replace, :binding => binding) do %> + <p>Product 1</p> + <p>Product 2</p> + <% end %> + * Added :field_name option to DateHelper#select_(year|month|day) to deviate from the year/month/day defaults #1266 [Marcel Molina] * Added JavascriptHelper#draggable_element and JavascriptHelper#drop_receiving_element to facilitate easy dragging and dropping through the script.aculo.us libraries #1578 [Thomas Fuchs] diff --git a/actionpack/lib/action_view/helpers/javascript_helper.rb b/actionpack/lib/action_view/helpers/javascript_helper.rb index b562e626fc..5e416060fa 100644 --- a/actionpack/lib/action_view/helpers/javascript_helper.rb +++ b/actionpack/lib/action_view/helpers/javascript_helper.rb @@ -160,6 +160,84 @@ module ActionView tag("input", options[:html], false) end + + # Returns a Javascript function (or expression) that'll update a DOM element according to the options passed. + # + # * <tt>:content</tt>: The content to use for updating. Can be left out if using block, see example. + # * <tt>:action</tt>: Valid options are :insert, :replace, :delete, :empty + # * <tt>:position</tt> If the :action is :insert, you can specify the following positions :before, :top, :bottom, :after. + # + # Examples: + # <%= javascript_tag(update_element_function( + # "products", :action => :insert, :position => :bottom, :content => "<p>New product!</p>")) %> + # + # <% replacement_function = update_element_function("products", :action => :replace) do %> + # <p>Product 1</p> + # <p>Product 2</p> + # <% end %> + # <%= javascript_tag(replacement_function) %> + # + # This method can also be used in combination with remote method call where the result is evaluated afterwards to cause + # multiple updates on a page. Example: + # + # # Calling view + # <%= form_remote_tag :url => { :action => "buy" }, :complete => evaluate_remote_response %> + # all the inputs here... + # + # # Controller action + # def buy + # @product = Product.find(1) + # end + # + # # Returning view + # <%= update_element_function( + # "cart", :action => :insert, :position => :bottom, + # :content => "<p>New Product: #{@product.name}</p>")) %> + # <% update_element_function("status", :action => :replace, :binding => binding) %> + # You've bought a new product! + # <% end %> + # + # Notice how the second call doesn't need to be in an ERb output block since it uses a block and passes in the binding + # to render directly. This trick will however only work in ERb (not Builder or other template forms). + def update_element_function(element_id, options, &block) + options[:content] = escape_javascript(options[:content] || capture(&block)) + + javascript_function = case options[:action] + when :insert + case options[:position] + when :before + "new Insertion.Before('#{element_id}', '#{options[:content]}')" + when :top + "new Insertion.Top('#{element_id}', '#{options[:content]}')" + when :bottom + "new Insertion.Bottom('#{element_id}', '#{options[:content]}')" + when :after + "new Insertion.After('#{element_id}', '#{options[:content]}')" + else + raise ArgumentError, "Invalid position, choose one of :before, :top, :bottom, :after" + end + + when :replace + "$('#{element_id}').innerHTML = '#{options[:content]}'" + + when :empty + "$('#{element_id}').innerHTML = ''" + + when :delete + "$('#{element_id}').parentNode.removeChild($('#{options[:target]}'))" + + else + raise ArgumentError, "Invalid action, choose one of :insert, :replace, :delete, :empty, :formfill" + end + + options[:binding] ? concat(javascript_function, options[:binding]) : javascript_function + end + + # Returns 'eval(request.responseText)' which is the Javascript function that form_remote_tag can call in :complete to + # evaluate a multiple update return document using update_element_function calls. + def evaluate_remote_response + "'eval(request.responseText)'" + end def remote_function(options) #:nodoc: for now javascript_options = options_for_ajax(options) diff --git a/actionpack/test/controller/capture_test.rb b/actionpack/test/controller/capture_test.rb new file mode 100644 index 0000000000..ad243c4587 --- /dev/null +++ b/actionpack/test/controller/capture_test.rb @@ -0,0 +1,39 @@ +require File.dirname(__FILE__) + '/../abstract_unit' + +class CaptureController < ActionController::Base + def self.controller_name; "test"; end + def self.controller_path; "test"; end + + def rescue_action(e) raise end +end + +CaptureController.template_root = File.dirname(__FILE__) + "/../fixtures/" + +class CaptureTest < Test::Unit::TestCase + def setup + @controller = CaptureController.new + + # enable a logger so that (e.g.) the benchmarking stuff runs, so we can get + # a more accurate simulation of what happens in "real life". + @controller.logger = Logger.new(nil) + + @request = ActionController::TestRequest.new + @response = ActionController::TestResponse.new + + @request.host = "www.nextangle.com" + end + + def test_simple_capture + get :capturing + assert_equal "Dreamy days", @response.body.strip + end + + def test_update_element_with_capture + get :update_element_with_capture + assert_equal( + "<script type=\"text/javascript\">$('products').innerHTML = '\\n <p>Product 1</p>\\n <p>Product 2</p>\\n'</script>" + + "\n\n$('status').innerHTML = '\\n <b>You bought something!</b>\\n'", + @response.body.strip + ) + end +end diff --git a/actionpack/test/fixtures/test/capturing.rhtml b/actionpack/test/fixtures/test/capturing.rhtml new file mode 100644 index 0000000000..1addaa40d9 --- /dev/null +++ b/actionpack/test/fixtures/test/capturing.rhtml @@ -0,0 +1,4 @@ +<% days = capture do %> + Dreamy days +<% end %> +<%= days %>
\ No newline at end of file diff --git a/actionpack/test/fixtures/test/update_element_with_capture.rhtml b/actionpack/test/fixtures/test/update_element_with_capture.rhtml new file mode 100644 index 0000000000..2d2ac84214 --- /dev/null +++ b/actionpack/test/fixtures/test/update_element_with_capture.rhtml @@ -0,0 +1,9 @@ +<% replacement_function = update_element_function("products", :action => :replace) do %> + <p>Product 1</p> + <p>Product 2</p> +<% end %> +<%= javascript_tag(replacement_function) %> + +<% update_element_function("status", :action => :replace, :binding => binding) do %> + <b>You bought something!</b> +<% end %>
\ No newline at end of file |