aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--actionpack/CHANGELOG9
-rw-r--r--actionpack/lib/action_view/helpers/javascript_helper.rb78
-rw-r--r--actionpack/test/controller/capture_test.rb39
-rw-r--r--actionpack/test/fixtures/test/capturing.rhtml4
-rw-r--r--actionpack/test/fixtures/test/update_element_with_capture.rhtml9
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