Add FBJS and the portfolio in action
Now that you have a canvas page view in place, it needs to display some stock prices. In this section add the FBJS to make that happen, and then run the stock broker application and see how it looks on Facebook.
Create a javascript directory under the "fb_stock_demo/app" directory and a file portfolio.js, as in Listing 24.
Listing 24. portfolio.js
var refreshTimer;
var REFRESH_DELAY = 1000;
var portfolioShares = [];
var tradeResult = null;
function init() {
updateTradeResult();
toggleRefresh();
}
|
First it creates some variables used in the rest of the script and the page - a refreshTimer object that will hold onto the Timer object used to trigger the AJAX calls, portfolioShares, the associative array of "ticker to shares" referred to in portfolio.php above, and tradeResult, whose value is also set in portfolio.php above to inform the FBJS of the result of a trade. init() is the function called at the very bottom of porfolio.php, called when the canvas page finishes loading.
Updating the trade result in the page
The first thing init() does is update the result of the trade on the page, as shown in Listing 25.
Listing 25. portfolio.js:
updateTradeResult()
function updateTradeResult() {
if (tradeResult) {
var resultElement = getTradeResultElement();
resultElement.toggleClassName (tradeResult > 0 ? 'increase' : 'decrease');
resultElement.setTextValue (formatMoney(tradeResult));
}
}
function getTradeResultElement() {
return document.getElementById('trade-result');
}
|
If the tradeResult variable is not null (i.e., was set by portfolio.php), updateTradeResult() looks up the 'trade-result' element introduced by portfolio.php in response to the trade. Then it uses two special FBJS DOM methods, toggleClassName() and setTextValue(). FBJS modifies the DOM and provides special DOM methods for accessing elements such as this one. You can easily add or remove a class name, or set the element's class name using toggleClassName(). SetTextValue() is another special method letting you set the plain text value of a DOM element. Instead of innerHTML, which Facebook does not support, you can do the equivalent using setTextValue() to set plain text, or setInnerFBML() to set the inner FBML of an element. Note that FBJS does not provide a getInnerFBML() to obtain the contents of an element, so it does not provide a full replacement for innerHTML. This is one reason for the use of a global portfolioShares FBJS array, as opposed to having the FBJS check the share element's innerHTML (or equivalent) to obtain the user's shares of the row's stock. It's also arguably better style to keep this information within the FBJS code, but is a demonstration of a possible workaround.
Note also that getTradeResultElement() calls document.getElementById() with the ID you used in portfolio.php. If you view the page source of the canvas page in the browser though, see that Facebook appends a code to the beginning of all your elements' Ids and CSS classes. It also prepends this code to all of your FBJS function name and variables. It does this to enclose your application in its own browser namespace, so that you can code as if you were in a closed system as opposed to having to worry about conflicts with the surrounding Facebook page.
Refreshing the page of stock prices once per second
To start the periodic refresh of the stock prices, add the toggleRefresh() function as in listing 26.
Listing 26. portfolio.js:
toggleRefresh()
function toggleRefresh() {
var button = document.getElementById('toggle-refresh');
if (refreshTimer) {
clearInterval(refreshTimer);
refreshTimer = null;
button.setTextValue('Start refresh');
} else {
refreshTimer = setInterval(refreshPrices, REFRESH_DELAY);
button.setTextValue('Pause refresh');
}
}
|
FBJS timers and interval timers work just like their counterparts in Javascript, except that setInterval() and setTimer() take a Function object, i.e., setInterval(refreshPrices), not a snippet of Javascript to execute as in setInterval('refreshPrices()'). ClearInterval() takes the repeating Timer object and stops it.
Making the AJAX call for the JSON feed of stock prices
Now that you have a repeating timer, add the refreshPrices() function that will make the AJAX call for the stock prices, as shown in Listing 27.
Listing 27. portfolio.js:
refreshPrices()
function refreshPrices() {
var ajax = new Ajax();
ajax.responseType = Ajax.JSON;
ajax.ondone = function (data) {
updateStockPrices (data.stocks);
}
ajax.onerror = onStockListError;
ajax.requireLogin = 1;
ajax.post (STOCK_PRICE_AJAX_URL);
}
|
This uses FBJS's easy-to-use Ajax object. You specify the response type (Ajax.JSON, Ajax.XML or Ajax.RAW), provide an ondone Function object which is called with the returned data, and an onerror Function object that is called if an error occurred while making the Ajax call. You can also specify whether the user must be logged into the application to make the Ajax call (in our case yes). Then you call its post() method with the URL to call, and it will call either your ondone or onerror function when the request completes. Because this instance uses the JSON responseType, it will call the ondone function with the JSON data translated into an actual Javascript object hierarchy corresponding to the JSON objects you specified in stockList.jsp.
UpdateStockPrices() itself runs through the array of stock objects, looking up each one's row in the table of stocks and updating its share price (and total dollar amount if the user owns shares of the stock), using setTextValue() as with the trade result. See Downloads for details.
At this point you can view the application in action. Go to your canvas page, log into the stock brokerage system (via LoginController), and the portfolio page will appear. It will pause one second, then start updating the prices and portfolio total up and down the page as shown in Figure 2.
Figure 2. The portfolio page in action
Enter a number of shares of a stock to buy, click Buy/Sell, and see the trade reflected on that row as shown in Figure 3.
Figure 3. Portfolio page after a trade
The trade also sent you a Facebook notification, as you can see in your Notifications page (see Figure 4).
Figure 4. Trade notifications
And the trade also posted an action to your Mini-Feed (see Figure 5) and possibly to your friends' News Feeds (depending on an unknown algorithm).
Figure 5. The trade Mini Feed action
The last piece of the application is the link on each stock row letting the user recommend the stock to friends on Facebook, using a Facebook request. This requires another PHP controller, RecommendStockToFriendsController (see source code), which puts the user's Facebook user ID, the application's URL, and the stock's ticker symbol on the model and renders the view recommendStockToFriendsView.php, as shown in Listing 28.
Listing 28. recommendStockToFriendsView.php
<?
$content = <<<FBML
<fb:name uid="$model['userId'] firstnameonly="true"</fb:name>
requests that you check out the hot stock "
<a href="$model['appUrl']">$model['ticker']</a>.
FBML;
?>
<fb:request-form
action="<?= $model['appUrl'] ?>"
method="post"
invite="true"
type="Hot Stock"
content="<?= htmlentities($content) ?>">
<fb:multi-friend-selector
showborder="false"
actiontext="Invite your friends to check out <?= $model['ticker'] ?>"/>
</fb:request-form>
|
The <fb:request-form> creates a Facebook request form, which when submitted will send a request to the specified users. The action attribute specifies where to take the user after they either submit the request or click the Skip button. The method specifies POST/GET as usual, and type specifies the name of the request, used on the button rendered as the <fb:request-form>'s submit button. The invite attribute changes some of the text used on the request and the submit button label. The content attribute contains the content to render in the request. Since the content is an attribute of an FBML tag all HTML entities must be escaped, as with htmlentities() here. This request's content just provides a link to the application's canvas page, but could do anything your application can do.
There are various tags that let the user select the friends to send the request to -- the one you use here is <fb:multi-friend-selector>, which appears as a scrolling list of all the user's friends with a handy typeahead search box, as shown in Figure 6.
Figure 6. The Recommend To Friends request form
Clicking Skip will take you back to the portfolio page (as per the <fb:request>'s action attribute). Selecting one or more friends and clicking 'Send Hot Stock Invitation' will present a confirmation screen of the request (see Figure 7).
Figure 7. Request confirmation screen
Clicking Send will send the request, and your friends will see the request on their Requests page as shown in Figure 8.
Figure 8. The request as seen by the friend
The content of the request is that specified in the Heredoc section of recommendStockToFriendsView.php.
The final step is to release your application into the wild. Go to your application's settings page, change Dev Mode to off so that people other than the developers can add the application. You will also want to create a static About Page for the Product Directory (and clean up some details in the application; for example, restricting access to various files and directories in the .htaccess file), and then click the Submit link (see Figure 9).
Figure 9. Submitting the application to the Product Directory
There will be a few fields to fill out (and your application will need at least 5 users before it can be submitted to the Product Directory), but then your application is available for public use.





