Advertisements
|
||
|
Engaging graphics and UI effects are the most visible characteristics of rich Internet applications (RIA). But focusing on outward appearance neglects an important rich-client feature: rich data.
Being able to rapidly filter and sort thousands of rows of data on the client, for example, can provide a much better experience in a business application than having to page through that data a few dozen rows at a time. Or, allowing a user to quickly visualize data in an interactive chart can prove more useful than presenting the user with a static chart image from the server.
Such interactive user experiences are possible because rich clients are able to efficiently fetch relatively significant amounts of data from a server, and perform presentation-related processing in the client's address space. In addition, a rich client can fetch data from multiple server applications, resulting in a mash-up.
Progressive enhancement, a termed coined by Steven Champeon, refers to the ability to enhance a traditional, Web 1.0-style application with rich-client features in a step-by-step fashion: Instead of having to buy into a rich-client technology whole-sale, it should be possible to introduce a better user experience on the client without a major re-write[1]. At the other end of the spectrum, a rich-client technology should provide capabilities that, when integrated deep into an application's architecture, take maximum advantage of the client's capabilities by, for example, making use of local graphic acceleration or the client environment's multi-core CPU architecture.
The examples in this article can be compiled and run either with Flex Builder 3, Adobe's Eclipse-based development environment, or with the open-source Flex SDK.
A 60-day free trial version of Flex Builder 3 can be downloaded from Adobe. If you already have Eclipse installed, you can download the Eclipse plug-in version of Flex Builder 3; otherwise, choose the full Eclipse install.
One way to analyze rich-client technologies is by how easily they can be introduced into existing enterprise applications. Flex, Adobe's open-source rich-client technology, offers many possibilities for progressively enhancing an existing enterprise application[2]. This article introduces five techniques a developer can use to improve the user experience, and possibly the visual appeal, of an existing enterprise Web application, using Flex:
The first technique is explained in the current part of this article; techniques (2) and (3) will be explained in Part II; and the last two techniques are illustrated in the concluding segment of this series.
In addition to these five techniques, a Flex client can also open a direct socket connection to network services. Direct socket connections, however, are an important tool mainly for streaming media-type applications, and are less helpful for enterprise development. Finally, Flex also supports SOAP. But SOAP invocation is similar to the HTTP technique, and SOAP applications these days are increasingly supplanted by RESTful HTTP invocations in many modern Web applications.
A subsequent article in this Artima series will describe integrating Flex with a Spring application; and another article will explore combining Flex and Scala, employing Scala Actors to scale the processing of incoming Flex data requests.
Each Flex integration technique is illustrated with a running example: An application for managing a bookstore's inventory, allowing a user to add and edit data about books, as well as to search for books.
A Flex application runs in the Flash Player. The Flash Player is typically installed as a browser plug-in, but Adobe's AIR runtime enables the desktop deployment of Flex applications as well [4][5]. A major piece of Flash Player is the ActionScript Virtual Machine that executes the Flash bytecodes to which a Flex application is compiled. Adobe open-sourced the ActionScript Virtual Machine in 2006, and continues its development as the Mozilla Tamarin project [6]. Flash Player, which is not open-source, adds application management, multimedia codex, runtime distribution and update, and other capabilities to the ActionScript VM.
The ActionScript VM provides many of the same techniques that made the Java VM perform so well over the years, such as just-in-time compilation and runtime bytecode optimization. While the ActionScript VM does not expose to developers the concurrent execution capabilities of multicore processors, it does internally parallelize code execution when possible [7].
A Flex application is written in ActionScript 3.0, an optionally statically typed language that fuses object-oriented and functional programming concepts [8]. ActionScript 3 is syntactically very similar to JavaScript—most JavaScript code compiles as valid ActionScript code—but also adds a Java-like, class-based OO programming model as well.
In addition to ActionScript, the Flex library provides an XML-based domain-specific language for UI design, MXML. The Flex compiler translates MXML code into ActionScript, and then compiles the ActionScript code into Flash bytecode. The open-source Flex SDK includes a rich set of UI widgets, and there is a thriving community of third-party Flex libraries as well.
Flash Player became popular mainly as a way to embed sophisticated vector graphics, or Flash movies, in Web pages. That design goal was carried over to each subsequent Flash Player version, making it equally easy to embed a Flex application into an existing HTML page.
Suppose the initial version of the bookstore application displayed the available inventory in a JSP page, using the HTML table
tag. If the store had several thousand titles in inventory, the user would either have to wait for a very long HTML table to render in the browser, or page through the inventory, say, twenty titles to a page.
Replacing the HTML table tag with a Flex DataGrid
component yields several advantages: the Flex datagrid can possibly load the entire inventory because it renders much faster than HTML; the DataGrid provides built-in table sorting, column reordering, and data filtering; DataGrid columns can have different formatters based on a column's data type; and DataGrid supports printing.
The following example contains the source code for a Flex application that displays such a datagrid:
<xml version="1.0" encoding="utf-8"?> <mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute"> <mx:DataGrid width="100%" height="100%" id="inventoryTable"/> </mx:Application>
This Flex application is described entirely in MXML, and consists of a declaration of a Flex DataGrid
component inside an Application
element. DataGrid
is an ActionScript class and, like any ActionScript class, it can be used from MXML as well: The compiler will figure out the ActionScript class name from the XML namespace and tag names.
The ActionScript language supports properties, and ActionScript object properties can be specified in MXML as XML attribute values. The DataGrid in the example is assigned an id
property, defining a way to reference the grid component. We also specify that the grid should expand to the entire width and height of the Flex application.
Compilation of this application results in a binary Flash file, BooksInventory.swf. The SWF (Shockwave Flash Format) can be embedded into an existing HTML page using the embed
or object
HTML tags [9]. Browser idiosynchracies make such embedding somewhat tricky, but the open-source SWFObject project automates most of the intricacies (FlexBuilder also generates the proper HTML tags for a SWF file) [10][11].
The following snippet shows embedding BooksInventory.swf into an HTML or JSP page using the SWFObject JavaScript API. Note how this technique progressively enhances the HTML page: the SWFObject's embedSWF()
method examines if the appropriate version of Flash Player—Version 10 in this case—is present; if not, the library will attempt to install Flash Player. It will then place the Flex application in a 600 by 400 pixel area, replacing the contents of the replaceContent
element.
<html> <head> <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1"/> <script type="text/javascript" src="swfobject.js"/> <script type="text/javascript"> swfobject.embedSWF("BooksInventory.swf", "replaceContent", "600", "400", "10.0.0", "playerProductInstall.swf"); </script> </head> <body> <div id="replaceContent"> <!-- Original HTML table here--> </div> </body> </html>
You can pass data from an HTML page into a Flex component using FlashVars. FlashVars is a parameter you specify to the Flash Player object, and it consists of name/value pairs separated by &
characters.
SWFObject makes such assignments easy as well: It allows JSON-style notation to specify FlashVars. The following example declares a JavaScript variable, flashvars
, with a single variable, bookInventory
. It then defines a JSON array as the value of the bookInventory
variable. The book inventory data array must be enclosed in a String, since a FlashVar can only be assigned a String value. That is tedious to do by hand, but the JSON data is likely generated from a data source on the server when creating the HTML or JSP (or PHP or Ruby on Rails) page. It is also possible to use an asynchronous Ajax call to retrieve the JSON data after the page completed loading in the client.
The flashvars
variable is passed to the Flex application as a parameter to embedSWF()
:
<html> <head> <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1"/> <script type="text/javascript" src="swfobject.js"/> <script type="text/javascript"> var flashvars = { bookInventory: '[' + '{"Title":"Programming in Scala", ' + '"Author(s)":"Odersky, Martin. Lex Spoon. Bill Venners",' + '"Year": 2008,' + '"Publisher": "Artima Press", ' + '"Price": 49.95, ' + '"Stock": 1520' + '}, ' + '{"Title":"Essential ActionScript 3", ' + '"Author(s)": "Moock, Colin", ' + '"Year": 2007,' + '"Publisher": "O\'Reilly", ' + '"Price": 47.95, ' + '"Stock": 2500' + '}, ' + '{"Title":"Programming Erlang", ' + '"Author(s)": "Armstrong, Joe", ' + '"Year": 2007,' + '"Publisher": "Pragmatic", ' + '"Price": 32.95, ' + '"Stock": 3525' + '}' + ']' }; swfobject.embedSWF("BooksInventory.swf", "replaceContent", "600", "400", "10.0.0", "playerProductInstall.swf", flashvars); </script> </head> <body> <div id="replaceContent"> <!-- Original HTML table here--> </div> </body> </html>
Upon initialization, we want the application to retrieve the bookInventory
flashVar, and assign its value as the table's data source. The following lines of code in the Flex application accomplish this:
<xml version="1.0" encoding="utf-8"?> <mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" creationComplete="onCreationComplete()"> <mx:Script> <![CDATA[ import com.adobe.serialization.json.JSON; private function onCreationComplete():void { var invParam:String = Application.application.parameters.bookInventory; inventoryTable.dataProvider = JSON.decode(invParam) as Array; } ]]> </mx:Script> <mx:DataGrid width="100%" height="100%" id="inventoryTable"/> </mx:Application>
As this example illustrates, a Flex application file often combines MXML for UI layout with ActionScript for coding interactivity or network data access. The ActionScript is placed inside CDATA sections of script
elements of the MXML file. The multi-language-per-file paradigm may appear peculiar at first. However, MXML is designed to mash with ActionScript quite well, taking advantage of ActionScript language features, such as properties and higher-order functions.
A Flex application dispatches several lifecycle events. One such event is creationComplete
, dispatched when the application has finished loading into the browser and the user interface is already rendered in Flash Player. In order to obtain the value of the bookInventory
FlashVar, we define an event listener for creationComplete
.
The event listener is the onCreationComplete()
function that obtains the string value of the bookInventory
, converts the JSON string to an Array
, and assigns that array as the table's data source.
This method illustrates an interesting aspect of ActionScript: Any ActionScript object, such as plain Object
, can act as a name/value map. For example, you can create a map in ActionScript thus:
var myMap:Object = new Object(); myMap.firstElement = "First element."; myMap.secondElement = 1234; var firstEl:String = String(myMap.firstElement); var secondEl:Number = Number(myMap.secondElement);
onCreationComplete()
in Listing 4 takes advantage of the fact that Flash Player populates an ActionScript object with FlashVar names and values, and makes that map available as Application.application.parameters
.
Listing 4 uses the open-source as3corelib project's JSON
class to convert the string parameter to an array of ActionScript objects [12]. When the resulting array is assigned as the table's data provider, the table populates with inventory data:
Converting JSON data results in an array of ActionScript objects. Each object is, in effect, a name/value pair. Flex's DataGrid component figures out column names from object property names. As Figure 2 shows, the map is not ordered by key, resulting in the column names displaying in seemingly random order. You can explicitly declare the columns, including column order, with these additions to the grid:
<mx:DataGrid width="100%" height="100%" id="booksInventory" variableRowHeight="true"> <mx:columns> <mx:DataGridColumn dataField="Title"/> <mx:DataGridColumn dataField="Author(s)" width="160" wordWrap="true"/> <mx:DataGridColumn dataField="Year" width="50" textAlign="center"/> <mx:DataGridColumn dataField="Publisher" width="120"/> <mx:DataGridColumn dataField="Price" width="60" textAlign="right"/> <mx:DataGridColumn dataField="Stock" width="60"/> </mx:columns> </mx:DataGrid>
Each column element references a property of the ActionScript objects as its dataField
. These plus some formatting changes result in a much better-looking data grid:
While simple, this application already presents several advantages over the HTML table tag: The columns are sortable, and the user can rearrange column order with a drag-and-drop gesture. And a Flex datagrid can display many thousands of rows without compromising performance either of display or sorting. Finally, if you try sorting the table by clicking on the column headers, you will note that numbers sort according to numeric, not alphabetic, order (123 comes before 1112)—Flex has correctly determined the data type of each column.
Friendlier formatting of the number columns could further improve the table. Flex provides the ability to assign a formatting function to a table column:
<mx:Script> <![CDATA[ ... private var currencyFormatter:CurrencyFormatter = new CurrencyFormatter(); private var numberFormatter:NumberFormatter = new NumberFormatter(); private function priceLabelFunction(obj:Object, col:DataGridColumn):String { return currencyFormatter.format(obj.Price); } private function stockLabelFunction(obj:Object, col:DataGridColumn):String { return numberFormatter.format(obj.Stock); } ]]> </mx:Script> ... <mx:DataGrid width="100%" height="100%" id="booksInventory" variableRowHeight="true"> <mx:columns> ... <mx:DataGridColumn dataField="Price" width="60" textAlign="right" labelFunction="priceLabelFunction"/> <mx:DataGridColumn dataField="Stock" width="60" textAlign="right" labelFunction="stockLabelFunction"/> </mx:columns> </mx:DataGrid> ...
Readers familiar with functional languages may recognize a form of structural typing here: Given the name of a function, for example, stockLabelFunction
, the Flex compiler locates a function with that name, with a set of required parameters—an Object
and a DataGridColumn
—and with a String
return type. The compiler then assigns that function to generate the text value of the data grid. If such a function can't be found in scope, the compiler raises an exception.
As a result of these label functions, the table now looks as follows:
Label functions, as well as custom column renderers, can also be used to create derived, or computed, columns on the client. For example, a computed column could display a checkbox to indicate whether a title is in stock, selecting the checkbox if the stock
property is greater than 0, unselecting it otherwise. Such client-computed data elements can enhance presentation without putting additional burden on the server.
In addition to flashvars, Flash Player exposes an entire API that enables rich interaction between a Flex application and its surrounding HTML page. For instance, you can invoke a Flex application's methods from JavaScript and, vice versa, a Flex application can call into JavaScript functions available on the DOM of its surrounding page.
Most enterprise Web applications provide some sort of HTML styling, often in the form of Cascading Style Sheets (CSS). An Flex component embedded in an HTML page can blend into its visual surroundings because Flex components can be styled with CSS. Each Flex component exposes a wide range of styling options, many of which are similar to HTML CSS properties.
The following stylesheet specifies a unique font for the application, a custom theme color, custom highlight color for the data grid rows, and centered table headings:
@font-face { src: url("../assets/MyriadWebPro.ttf"); font-family: main; font-style: normal; font-weight: normal; } @font-face { src: url("../assets/MyriadWebPro-Bold.ttf"); font-family: main; font-style: normal; font-weight: bold; } Application { font-family: main; font-size: 12; theme-color: #0066B3; color: #111111; } DataGrid { rollOverColor: #FFFFFF; selectionColor: #FFFF66; } .centeredHeader { text-align: center; font-weight: bold; }
The Flex application needs to reference this CSS file for the application to assume the specified style properties:
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" creationComplete="onCreationComplete()"> <mx:Style source="style.css"/> ...
Style sheet classes can be assigned to individual Flex components as well. To apply the centeredHeader
class to the data grid, we add the following:
<mx:DataGrid width="100%" height="100%" id="booksInventory" variableRowHeight="true" headerStyleName="centeredHeader">
Applying these style elements results in the following application:
In his Artima blog, Bruce Eckel described embedding a Flex component into an HTML page in this manner as the process of "hybridizing HTML[13]." This technique can be used to enhance any HTML page, and is agnostic to server-side technology.
Because a Flex application executes inside a Web page, all integration of Flex is based on "hybridizing HTML" with Flex components: Many parts of a Web application are best rendered with HTML, using traditional HTML rendering techniques, such as server-side templates or JSP pages. However, some components, such as the data editor in this example, can be better implemented in Flex, and then simply embedded in an HTML page.
Passing data to a Flex application via FlashVars is easy, but it has an important drawback: Since the value of FlashVars must be a string value, you are limited in the amount of data you can specify in that manner. Most browsers limit the size of a JavaScript String to 65KB. The next installment of this article will show how to remove that limit by delegating data loading to the Flex application.
[1] Gustafson, Aaron. "Understanding Progressive Enhancement." A List Apart, October 7, 2008. Accessed on April 8, 2009.
http://www.alistapart.com/articles/understandingprogressiveenhancement
[2] Adobe's Flex framework
http://www.adobe.com/products/flex/
[3] BlazeDS
http://www.adobe.com/products/flex/
[4] Flash Player
http://www.adobe.com/products/flashplayer/
[5] Adobe AIR
http://www.adobe.com/products/air/
[6] Mozilla Tamarin VM
http://www.mozilla.org/projects/tamarin/
[7] Uro, Tinic. "Multi core support." June 12, 2007. Access on April 6, 2009.
http://www.kaourantin.net/2007/06/multi-core-support.html
[8] Adobe Corporation. ActionScript 3 Language Specification 2006.
http://livedocs.adobe.com/specs/actionscript/3/wwhelp/wwhimpl/js/html/wwhelp.htm
[9] SWF. Wikipedia. Accessed on April 10, 2009.
http://en.wikipedia.org/wiki/SWF
[10] SWFObject library
http://code.google.com/p/swfobject/
[11] Flex Builder 3
http://www.adobe.com/products/flex/features/flex_builder/
[12] as3corelib library
http://code.google.com/p/as3corelib/
[13] Eckel, Bruce. "Hybridizing HTML." November 8, 2007. Accessed on April 10, 2009.
http://www.artima.com/weblogs/viewpost.jsp?thread=213902
Have an opinion? Readers have already posted 3 comments about this article. Why not add yours?
Frank Sommers is Editor-in-Chief of Artima Developer. He also serves as chief editor of the IEEE Technical Committee on Scalable Computing's newsletter, and is an elected member of the Jini Community's Technical Advisory Committee. Prior to joining Artima, Frank wrote the Jiniology and Web services columns for JavaWorld.
Artima provides consulting and training services to help you make the most of Scala, reactive
and functional programming, enterprise systems, big data, and testing.