Ajax, Rails & Facebook example
This post shows how you can do Ajax within a Facebook Canvas using Ruby on Rails. This builds on my other facebooker examples (Facebooker Quick Start and Creating Facebook forms with Facebooker) , but the examples here don't really make use of any Facebooker specific features because I wanted to show the basics of how things worked, you can in several cases use Facebooker helpers instead of the manual way I have done things.
First some background, since you are in a fbml canvas within a Facebook applications (diagram), and all requests are proxied through facebook & all javascript is facebook-ized (turned into fbjs), there are some limits as to what you can do. I suggest before trying anything you read FBJS and FBJS Local Proxy.
Example #1 - Update a DIV with raw text & pass a value to the server - you must update the CALLBACK_URL_HERE with your callback_url & this example assumes your controller is called face_controller.
---------------------------------------------------------------------------------------------
View:
<script>
function do_ajax(div,val) {
var ajax = new Ajax();
ajax.responseType = Ajax.RAW;
ajax.ondone = function(data) {
var p = document.getElementById(div);
p.setTextValue(data);
}
ajax.requireLogin = true;
ajax.post([CALLBACK_URL_HERE]/face/example1?someval=' + val);
}
</script>
<a href="#" onclick="do_ajax('ajax1','1234'); return false;">Update a DIV</a><br />
<div>
<span id="ajax1"></span>
</div>
<BR/>
---------------------------------------------------------------------------------------------
Controller:
def example1
someval = params[:someval]
logger.debug "I got the update task checked!"
logger.debug "someval: " + someval.to_s
render :text => "this is some raw text", :layout => false
end
Example #2 - Make a remote call when a checkbox is checked and unchecked and use fbml to update div. Notice the subtle changes, we are using response type of FBML and we are setting the div using setInnerFBML, on the server we are returning text in FBML markup.
---------------------------------------------------------------------------------------------
View:
<script>
function do_ajax(div,val) {
var ajax = new Ajax();
ajax.responseType = Ajax.FBML;
ajax.ondone = function(data) {
var p = document.getElementById(div);
p.setInnerFBML(data);
}
ajax.requireLogin = true;
var cbox = document.getElementById('check_me_now');
var isChecked = cbox.getChecked();
ajax.post('http://[CALLBACK_URL_HERE]/face/example2?ischecked=' + isChecked);
}
</script>
<span id="ajax1"></span>
<BR/>
<INPUT TYPE=CHECKBOX NAME="complete" id="check_me_now" onClick="do_ajax('ajax1','1234');" > Take out the trash
<BR/>
---------------------------------------------------------------------------------------------
Controller:
def example2
is_checked = params[:ischecked]
if is_checked == "true"
txt_to_render = "<fb:success message=\"Task has been marked complete!\" />"
else
txt_to_render = "<fb:success message=\"Task has been marked incomplete!\" />"
end
render :text => txt_to_render, :layout => false
end
Example #3 - Make Ajax calls using local proxy and JSON protocol. In order to do this, your customers must have pretty recent versions of browsers with the flash plugin installed & you must have servers listening on port 80 (even to do dev or test). Fro JSON notice how we use data.content to access the content tag in the JSON.
---------------------------------------------------------------------------------------------
crossdomain.xml (must be in your rails public directory)
<?xml version="1.0"?>
<!DOCTYPE cross-domain-policy SYSTEM "http://www.macromedia.com/xml/dtds/cross-domain-policy.dtd">
<cross-domain-policy>
<allow-access-from domain="apps.facebook.com" />
<allow-access-from domain="apps.*.facebook.com" />
</cross-domain-policy>
---------------------------------------------------------------------------------------------
View:
<script>
function do_ajax(div,val) {
var ajax = new Ajax();
ajax.useLocalProxy = true
ajax.responseType = Ajax.JSON;
ajax.ondone = function(data) {
var p = document.getElementById(div);
p.setInnerXHTML(data.content);
}
ajax.requireLogin = true;
ajax.post('http://[CALLBACK_URL_HERE]/face/example3);
}
</script>
<a href="#" onclick="do_ajax('ajax1','1234'); return false;">Update a DIV</a><br />
<div>
<span id="ajax1"></span>
</div>
<BR/>
<fb:local-proxy/>
Controller:
def example3
logger.debug "I got to example3!"
render :text => "{\"content\":\"<p>From Server</p>\"}", :layout => false
end
*Note, on this example I didn't see any parameters coming in from setting requireLogin = true, I have opened a question on this in the forum and will update when I know more.
Troubleshooting:
- I get the error in the browser: "XML Parsing Error: syntax error Location: http://apps.facebook.com/gottakeepup/test Line Number 1, Column 1:From Server "
If your using setInnerXHTML to put the response, make sure the response is valid XHTML, if you are getting from server, change it to <p> from server </p> - I get a bunch of text that starts with <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
You are probably using the canvas url instead of the callback url - I see the call to to my server, but my UI is not getting updated properly
Install firebug and debug the fbjs (its the javascript generated with the numeric prefixes)
Hi Joel,
After trying the examples from the facebook developer's wiki without success, I found your examples and I tried example 1. I have to say that your examples are failing for me as well. There is a problem with executing the ajax.post() because I am not seeing the post request on the server side. I replaced the face controller with a controller named ajax and a method called example1. When I call the method from my browser, outside of facebook I get the text correctly. I fear that there is something tricky with my call back url or something with the facebook layout. BTW, I am using rfacebook and not facebooker which I think should not matter. I would appreciate your thoughts on this.
Thank you,
Ricardo
Posted by: Ricardo | February 18, 2008 at 10:23 PM
Hi Joel,
I had a typo in my javascript that was impeding the post calls.
Thanks anyway and thanks for this effort of documenting your learning process.
It is helping me a lot!
Ricardo
Posted by: Ricardo | February 19, 2008 at 11:29 AM