Rhodes provides another utility for connecting to backend services besides the SyncEngine called AsyncHttp. Your application can use the AsyncHttp library to interact with web services, pull remote images, etc.
Refer to the AsyncHttp API methods to asynchronously make calls to http(s) services.
You can perform an HTTP GET request to the specified :url. You can also provide an optional hash of :headers and :callback_param.
Rho::AsyncHttp.get( :url => "http://www.example.com", :headers => {"Cookie" => cookie}, :callback => (url_for :action => :httpget_callback) )
Example using Basic Auth:
Rho::AsyncHttp.get( :url => "http://www.example.com", :headers => {"Cookie" => cookie}, :callback => (url_for :action => :httpget_callback), :authentication => { :type => :basic, :username => "john", :password => "secret" } )
Example of synchronous call:
result = Rho::AsyncHttp.get( :url => "http://www.apache.org/licenses/LICENSE-2.0" ) @get_result = res["body"]
WARNING! Do NOT use synchronous calls unless you know what you are doing. This is a blocking call and will cause your UI to freeze.
You can perform an HTTP POST request to the specified :url. As with get, you can specify optional arguments.
# :post HTTP POST body to send with request. # :http_command (optional) Use different HTTP method # (i.e. "put"). Rho::AsyncHttp.post( :url => "https://www.example.com", :headers => {"Cookie" => cookie}, :body => "username=john&password=secret", :callback => url_for(:action => :httppost_callback), :callback_param => "post=complete" )
You can download a file to the specified filename.
file_name = File.join(Rho::RhoApplication::get_base_app_path, "test.jpg") # :filename Full path to download file target. Rho::AsyncHttp.download_file( :url => "http://www.google.com/images/logos/ps_logo2.png", :filename => file_name, :headers => {}, :callback => url_for(:action => :httpdownload_callback), )
Upload the specified file using HTTP POST:
file_name = File.join(Rho::RhoApplication::get_base_app_path, "myfile.txt") # :filename Full path to download file target. # :post HTTP POST body to send with request. Rho::AsyncHttp.upload_file( :url => "http://example.com/receive_file", :filename => file_name, :body => "" #=> leave blank, AsyncHttp will fill in multipart body :headers => {"Content-Type"=>"text/plain"}, #=> used as body text content type :callback => url_for(:action => :httpupload_callback), :callback_param => "" )
You can also send multiple files in a single upload_file request:
# :multipart Array of hashes containing # file information. # # :multipart[:filename] Name of file to be uploaded. # # :multipart[:filename_base] (optional) Base directory containing # the :filename. # :multipart[:name] (optional) File type, defaults # to "blob". # # :multipart[:content_type] (optional) Content-Type header, # defaults to "application/octet-stream". Rho::AsyncHttp.upload_file( :url => "some_url", :multipart => [ { :filename => file_name, # if missed base name from file path used :filename_base => "files_to_upload", :name => "image", :content_type => "application/octet-stream" }, # You can specify file content inline. { :body => "upload test", :name => "upload_body_test", :content_type => "plain/text" } ] )
As you noticed with each of the code samples above, we specified a :callback action. This callback will execute when the AsyncHttp request is completed.
Refer to AsyncHttp API for a discussion of the callback parameters.
Adding an animated transition to an AsyncHttp request requires some small setup and is useful for displaying a smoother user experience.
To enable an animated transition, the controller action must set a "Wait-Page" response header after making the AsyncHttp call. The response header tells the user interface that an AsyncHttp request has been spawned and that the rendered view should be treated as a transient page, it will not be added to the navigation history.
def async_show Rho::AsyncHttp.get( :url => "http://rhostore.herokuapp.com/products/#{@params["product_id"]}.json", :callback => url_for(:action => :show_callback), ) @response["headers"]["Wait-Page"] = "true" render :action => :waiting end
This example renders a waiting screen while awaiting a response from the AsyncHttp request. The :waiting page is transient and will not be added to the navigation history, which means clicking back won’t open the page.
The AsyncHttp callback can render the response by calling render_transition. This function is defined in ApplicationHelper so make sure you include it in your controller. The render_transition function works much like render except that it will animate a transition from the previous page.
Below, a product model is created using the response from the web service and then calling render_transition, which leverages the show view template:
include ApplicationHelper def show_callback if @params["status"] == "ok" @product = Product.new(@params["body"]["product"]) @product.object = @product.id render_transition :action => :show else # In this example, an error just navigates back to the index w/o transition. WebView.navigate url_for :action => :index end end
To disable jQuery Mobile page caching (by default jQuery Mobile cachepages in the DOM) globally, look for cache control options in jQuery Mobile documentation
You can disable page caching globally by using jQuery Mobile initialization option in layout.erb file:
$.mobile.page.prototype.options.domCache = true;
Also, you can disable caching on exact page transition with data-dom-cache attribute, like that: link text
If you deploy to platforms that don"t handle animated transitions (like Windows Mobile and BlackBerry), the controller will need to handle both cases. In your AsyncHttp request, you"ll need to set the callback_param with the @request variable.
There’s a helper function called caller_request_hash_to_query defined in ApplicationHelper that you can invoke. The returned value is a string that looks like “_request=<json_request> is the URL-encoded JSON representation of the @request value. This parameter is used to give the callback function some context of whether the user interface made the request with or without transition enabled.
include ApplicationHelper def async_show Rho::AsyncHttp.get( :url => "http://rhostore.herokuapp.com/products/#{@params["product_id"]}.json", :callback => url_for(:action => :show_callback), :callback_param => caller_request_hash_to_query ) @response["headers"]["Wait-Page"] = "true" render :action => :waiting end
In your callback function, the first thing you need to do is invoke caller_request_query_to_hash (also defined in ApplicationHelper) that deserializes the @request query parameter value passed in via callback_param shown in the example above. The function sets a @caller_request in the current context. You can then use it to determine if the user interface had transition enabled by inspecting the "Transition-Enabled" request header. For transitions, call render_transition, otherwise call WebView.navigate.
def show_callback caller_request_query_to_hash if @params["status"] == "ok" @product = Product.new(@params["body"]["product"]) @product.object = @product.id if @caller_request["headers"]["Transition-Enabled"] == "true" render_transition :action => :show else WebView.navigate( url_for(:action => :show, :id => @product.object) ) end else WebView.navigate( url_for(:action => :index) ) end end
Here is a controller in the Rexml sample from the System API Samples. It makes a AsyncHttp.get call to a test web service. Then it parses the web service response with rexml and displays the result.
def webservicetest Rho::AsyncHttp.get( :url => "http://rhostore.herokuapp.com/products.xml", :callback => url_for(:action => :httpget_callback), ) render :action => :wait end def get_res @@get_result end def get_error @@error_params end def httpget_callback if @params["status"] != "ok" @@error_params = @params WebView.navigate( url_for(:action => :show_error) ) else @@get_result = @params["body"] begin require "rexml/document" doc = REXML::Document.new(@@get_result) puts "doc : #{doc}" rescue Exception => e puts "Error: #{e}" @@get_result = "Error: #{e}" end WebView.navigate( url_for(:action => :show_result) ) end end def show_result render :action => :webservicetest, :back => "/app/RexmlTest" end