Flex 3 with Java

August 24, 2009

Adobe Flex

«»

Working with XML objects

The XML class provides many useful methods to work with XML objects, such as
the appendChild() and prependChild() methods to add an XML element to the
beginning or end of an XML object, as shown in the following example:


	var node1:XML = <middleInitial>B</middleInitial>
	var node2:XML = <lastName>Kore</lastName>
	var root:XML = <personalInfo></personalInfo>
	root = root.appendChild(node1);
	root = root.appendChild(node2);
	root = root.prependChild(<firstName>Satish</firstName>);

The output is as follows:


	<personalInfo>
		<firstName>Satish</firstName>
		<middleInitial>B</middleInitial>
		<lastName>Kore</lastName>
	</personalInfo>

You can use the insertChildBefore() or insertChildAfter() method to add a
property before or after a specified property, as shown in the following example:


	var x:XML = <count>
		<one>1</one>
		<three>3</three>
		<four>4</four>
		</count>;
	x = x.insertChildBefore(x.three, "<two>2</two>");
	x = x.insertChildAfter(x.four, "<five>5</five>");
	trace(x.toXMLString());

The output of the above code is as follows:


	<count>
		<one>1</one>
		<two>2</two>
		<three>3</three>
		<four>4</four>
		<five>5</five>
	</count>

Using XML as dataProvider

One of the powerful features of the XML object is to use it as a dataProvider
for your component that allows you to tie XML directly with your component’s
data model. Let’s see how we can use XML as the dataProvider of a DataGrid
component to display complex data.


	private var xmlData:XML =
	<books>
		<book ISBN="184719530X">
			<title>Building Websites with Joomla! 1.5</title>
			<author>
				<lastName>Hagen</lastName>
				<firstName>Graf</firstName>
			</author>
			<image>../assets/images/184719530X.png</image>
			<pageCount>363</pageCount>
			<price>Rs.1,247.40</price>
			<description>The best-selling Joomla! tutorial</description>
		</book>
	</books>;
	private function getAuthorName(item:Object, column:DataGridColumn):
	String {
		var xml:XML = XML(item);
		return item.author.firstName +" "+ item.author.lastName;
	}

We have created an XML object with a complex XML structure. Now, we will
tie this with the DataGrid component using data binding, as shown in the
following example:


	<mx:Panel title="XML dataProvider example" width="666" height="149">
		<mx:DataGrid id="dgGrid" dataProvider="{xmlData.book}"
			height="100%" width="100%">
			<mx:columns>
				<mx:DataGridColumn headerText="ISBN" dataField="@ISBN"/>
				<mx:DataGridColumn headerText="Title" dataField="title"/>
				<mx:DataGridColumn headerText="Author"
					labelFunction="getAuthorName"/>
				<mx:DataGridColumn headerText="Price" dataField="price"/>
				<mx:DataGridColumn headerText="Description"
					dataField="description"/>
			</mx:columns>
		</mx:DataGrid>
	</mx:Panel>

In the code above, we have created a DataGrid component with a set of columns to
display data from the XML object. Notice that we have used {} (curly braces) to bind
the XML object’s specific nodes with the DataGrid. This means {xmlData.book}
specifies that the DataGrid will use the book node(s) and its children nodes as source
of its data.

We have used the DataGridColumn’s dataField property. The dataField property
is the name of the element or attribute in the XML data provider item associated with
the column. For example, to display the book’s title, the dataField property is set
to title.

The labelFunction function is used to specify the name of a function. The function
will be called and the return value is used to display in this column. For example,
sometimes you might need to customize how your data gets displayed. In this case,
labelFunction is used to concatenate the firstName and lastName element’s
values and return them as a single string.

The labelFunction function takes two parameters: the DataGrid item as an object
and the DataGrid column.

The output looks as follows:

You can also use the XMLListCollection object as the dataProvider of your
component. The XMLListCollection object can hold the XMLList objects and
provides a set of methods that lets you access, sort, filter, and modify the data items
in that data object. This is very helpful if you are working with dynamic XML data.
It can be used to dynamically add and remove items from the data provider and its
representation in the UI control.

The following example shows how to work with XMLListCollection and
dynamically add new elements in it:


	private var xmlData:XML =
		<books>
			<book ISBN="184719530X">
				<title>Building Websites with Joomla! 1.5</title>
				<author>
					<lastName>Hagen</lastName>
					<firstName>Graf</firstName>
				</author>
				<image>../assets/images/184719530X.png</image>
				<pageCount>363</pageCount>
				<price>Rs.1,247.40</price>
				<description>The best-selling Joomla! tutorial</description>
			</book>
		</books>;

	private var newBookElement:XML =
		<book ISBN="1847196160">
			<title>Drupal 6 JavaScript and jQuery</title>
			<author>
				<lastName>Matt</lastName>
				<firstName>Butcher</firstName>
			</author>
			<image>../assets/images/1847196160.png</image>
			<pageCount>250</pageCount>
			<price>Rs.1,108.80</price>
			<description>Drupal 6 modules and themes</description>
		</book>;

	private var xmlListCollection:XMLListCollection =
	new XMLListCollection(xmlData.book);

Note that xmlData.book returns an XMLList object with all book elements.

You can use the addItem() method of the X MLListCollection class to add
newBookElement to it, as shown here:


	xmlListCollection.addItem(newBookElement);

And you can set the xmlListCollection object as the dataProvider of your
DataGrid using {} (curly braces) data-binding expression.

Loading external XML documents

You can use the URLLoader class to load external data from a URL. The URLLoader
class downloads data from a URL as text or binary data. In this section, we will see
how to use the URLLoader class for loading external XML data into your application.
You can create a URLLoader class instance and call the load() method by passing
URLRequest as a parameter and register for its complete event to handle loaded data.
The following code snippet shows how exactly this works:


	private var xmlUrl:String = "http://www.foo.com/rssdata.xml";
	private var request:URLRequest = new URLRequest(xmlUrl);
	private var loader:URLLoader = new URLLoader(;
	private var rssData:XML;
	
	loader.addEventListener(Event.COMPLETE, completeHandler);
	
	loader.load(request);

	private function completeHandler(event:Event):void {
		rssData = XML(loader.data);
		trace(rssData);
	}

Let’s see one quick complete sample of loading RSS data from the Internet:


	<?xml version="1.0" encoding="utf-8"?>
	<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"
	  creationComplete="loadData();">
		<mx:Script>
			<![CDATA[
				import mx.collections.XMLListCollection;
				private var xmlUrl:String = "http://sessions.adobe.com/
											360FlexSJ2008/feed.xml";
				private var request:URLRequest = new URLRequest(xmlUrl);
				private var loader:URLLoader = new URLLoader(request);
				  [Bindable]
				private var rssData:XML;
	
				private function loadData():void {
					loader.addEventListener(Event.COMPLETE, completeHandler);
					loader.load(request);
				}
				private function completeHandler(event:Event):void {
					rssData = new XML(loader.data);
				}
			]]>
		</mx:Script>
		<mx:Panel title="RSS Feed Reader" width="100%" height="100%">
			<mx:DataGrid id="dgGrid" dataProvider="{rssData.channel.item}"
												height="100%" width="100%">
				<mx:columns>
					<mx:DataGridColumn headerText="Title" dataField="title"/>
					<mx:DataGridColumn headerText="Link" dataField="link"/>
					<mx:DataGridColumn headerText="pubDate"
										dataField="pubDate"/>
					<mx:DataGridColumn headerText="Description"
										dataField="description"/>
				</mx:columns>
			</mx:DataGrid>
			<mx:TextArea width="100%" height="80"
			   text="{dgGrid.selectedItem.description}"/>
		</mx:Panel>
	</mx:Application>

In the code above, we are loading RSS feed from an external URL and displaying it in
DataGrid by using data binding.

Output:

An example: Building a book explorer

By this time, you would be comfortable in writing Flex applications by using many
features of Flex and ActionScript, which you have learned in the previous chapters.
In this section, we will build something more complicated and interesting by using
many features, including custom components, events, data binding, E4X, loading
external XML data, and so on.

We will build a sample books explorer, which will load a books catalog from an
external XML file and allow the users to explore and view details of books. We will
also build a simple shopping cart component, which will list books that a user would
add to cart by clicking on the add to cart button.

Create a new Flex project using Flex Builder. Once the project is created, create
an \assets\images\ folder under its src folder. This folder will be used to store
images used in this application. Now start creating the following source files into
its source folder.

Let’s start by creating a simple book catalog XML file as follows:


bookscatalog.xml:
	<books>
		<book ISBN="184719530X">
			<title>Building Websites with Joomla! 1.5</title>
			<author>
				<lastName>Hagen</lastName>
				<firstName>Graf</firstName>
			</author>
			<image>../assets/images/184719530X.png</image>
			<pageCount>363</pageCount>
			<price>Rs.1,247.40</price>
			<description>The best-selling Joomla! tutorial guide updated for
		the latest 1.5 release </description>
		</book>
		<book ISBN="1847196160">
			<title>Drupal 6 JavaScript and jQuery</title>
			<author>
				<lastName>Matt</lastName>
				<firstName>Butcher</firstName>
			</author>
			<image>../assets/images/1847196160.png</image>
			<pageCount>250</pageCount>
			<price>Rs.1,108.80</price>
			<description>Putting jQuery, AJAX, and JavaScript effects into
		your Drupal 6 modules and themes</description>
		</book>
		<book ISBN="184719494X">
			<title>Expert Python Programming</title>
			<author>
				<lastName>Tarek</lastName>
				<firstName>Ziadé</firstName>
			</author>
			<image>../assets/images/184719494X.png</image>
			<pageCount>350</pageCount>
			<price>Rs.1,247.4</price>
			<description>Best practices for designing, coding, and
		distributing your Python software</description>
		</book>
		<book ISBN="1847194885">
			<title>Joomla! Web Security</title>
			<author>
				<lastName>Tom</lastName>
				<firstName>Canavan</firstName>
			</author>
			<image>../assets/images/1847194885.png</image>
			<pageCount>248</pageCount>
			<price>Rs.1,108.80</price>
			<description>Secure your Joomla! website from common security
		threats with this easy-to-use guide</description>
		</book>
	</books>

The above XML file contains details of individual books in an XML form. You can
also deploy this file on your web server and specify its URL into URLRequest while
loading it.

Next, we will create a custom event which we will be dispatching from our custom
component. Make sure you create an events package under your src folder in Flex
Builder called events, and place this file in it.


	AddToCartEvent.as
		package events
		{
			import flash.events.Event;

			public class AddToCartEvent extends Event
			{
				public static const ADD_TO_CART:String = "addToCart";
				public var book:Object;

				public function AddToCartEvent(type:String, bubbles:
			Boolean=false, cancelable:Boolean=false)
				{
					super(type, bubbles, cancelable);
				}
			}
		}

This is a simple custom event created by inheriting the flash.events.Event
class. This class defines the ADD_TO_CART string constant, which will be used as
the name of the event in the addEventListener() method. You will see this in the
BooksExplorer.mxml code. We have also defined an object to hold the reference of
the book which the user can add into the shopping cart. In short, this object will hold
the XML node of a selected book.

Next, we will create the MXML custom component called BookDetailItemRenderer.
mxml. Make sure that you create a package under your src folder in Flex Builder called
components, and place this file in it and copy the following code in it:


	<?xml version="1.0" encoding="utf-8"?>
	<mx:HBox xmlns:mx="http://www.adobe.com/2006/mxml"
	  cornerRadius="8" paddingBottom="2" paddingLeft="2"
	  paddingRight="2" paddingTop="2">
		<mx:Metadata>
			[Event(name="addToCart", type="flash.events.Event")]
		</mx:Metadata>
		
		<mx:Script>
			<![CDATA[
				import events.AddToCartEvent;
				import mx.controls.Alert;
				
				  [Bindable]
				  [Embed(source="../assets/images/cart.gif")]
				public var cartImage:Class;
				
				private function addToCardEventDispatcher():void {
				var addToCartEvent:AddToCartEvent = new AddToCartEvent
				("addToCart", true, true);
					addtoCartEvent.book = data;
					dispatchEvent(addtoCartEvent);
				}
			]]>
		</mx:Script>
		<mx:HBox width="100%" verticalAlign="middle" paddingBottom="2"
		  paddingLeft="2" paddingRight="2" paddingTop="2" height="100%"
		  borderStyle="solid" borderThickness="2" borderColor="#6E6B6B"
		  cornerRadius="4">
			<mx:Image id="bookImage" source="{data.image}" height="109"
			  width="78" maintainAspectRatio="false"/>
			<mx:VBox height="100%" width="100%" verticalGap="2"
			  paddingBottom="0" paddingLeft="0" paddingRight="0"
			  paddingTop="0" verticalAlign="middle">
				<mx:Label id="bookTitle" text="{data.title}"
				  fontSize="12" fontWeight="bold"/>
				<mx:Label id="bookAuthor" text="By: {data.author.
				  lastName},{data.author.firstName}" fontWeight="bold"/>
				<mx:Label id="coverPrice" text="Price: {data.price}"
				  fontWeight="bold"/>
				<mx:Label id="pageCount" text="Pages: {data.pageCount}"
				  fontWeight="bold"/>
				<mx:HBox width="100%" backgroundColor="#3A478D"
				  horizontalAlign="right" paddingBottom="0" paddingLeft="0"
				  paddingRight="5" paddingTop="0" height="22"
				  verticalAlign="middle">
					<mx:Label text="Add to cart " color="#FFFFFF"
					  fontWeight="bold"/>
					<mx:Button icon="{cartImage}" height="20" width="20"
					  click="addToCardEventDispatcher();"/>
				</mx:HBox>
			</mx:VBox>
		</mx:HBox>
	</mx:HBox>

The above custom component will be used as an itemRenderer to display
books’ details from an XML file. In this example, we have created a custom
MXML-based component. This custom component dispatches a custom event
called AddToCartEvent when a user clicks on the add to cart button. Notice that
when we are dispatching an event, we are setting its bubbles argument (second
argument in the AddToCartEvent constructor, which is inherited from the
flash.events.Event class) to true. This is very important in order to bubble this
event up to its parent where we will write an event listener for it. (For information
on bubbling, please see the Event propagation section in Chapter 3). You will see how
to write an event listener on the parent to handle the event dispatched by its children
in the BooksExplorer.mxml code. At the end, this custom component will be used
as ItemRenderer of the TileList component to display books. So we are using the
data property of the itemRenderer instance to access XML nodes and properties
using the E4X technique. The data property is implicitly available in item renderers
that can be used to access content locally. For information on ItemRenderers and
how to use its data property, please see the Understanding Flex item renderers section
in Chapter 2.

Next, we will create the main application and layout the book explorer user interface,
and then we will write business logic for loading XML data and display it in the
custom component, and so on.

BooksExplorer.mxml:


	<?xml version="1.0" encoding="utf-8"?>
	<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" creationCom
	  plete="init()"
		xmlns:components="components.*" layout="horizontal">
		
		<mx:Script>
		<![CDATA[
			import mx.collections.XMLListCollection;
			import mx.formatters.CurrencyFormatter;
			import mx.collections.ArrayCollection;
			import mx.effects.Blur;
			import events.AddToCartEvent;
			import components.BookDetailItemRenderer;
			import mx.controls.Alert;
			
			private var loader:URLLoader;
			private var request:URLRequest;
			  
			  [Bindable]
			private var xmlData:XML;
			
			  [Bindable]
			private var selectedBook:XML;
			  [Bindable]
			private var shoppingCart:ArrayCollection;

			private function init():void {
				tileList.addEventListener(AddToCartEvent.ADD_TO_CART,
				addToCartHandler);
				shoppingCart = new ArrayCollection();

				request = new URLRequest("./bookscatalog.xml");
				
				loader = new URLLoader();
				loader.addEventListener(Event.COMPLETE, completeHandler);
				try {
					loader.load(request);
				} catch (error:SecurityError) {
					trace("A SecurityError has occurred.");
				} catch(error:Error) {
					trace("An Unknown Error has occurred. ["+error.
					message+"]");
				}
			}
			private function completeHandler(event:Event):void {
				xmlData = new XML(loader.data);
			}
			private function addToCartHandler(event:AddToCartEvent):void
			{
				shoppingCart.addItem(XML(event.book));
			}
			private function itemSelected(event:Event):void {
				selectedBook = XML(tileList.selectedItem);
			}
		]]>
		</mx:Script>
		<mx:VBox width="100%" height="100%" verticalAlign="bottom">
			<mx:HBox width="100%" height="70%">
				<mx:Panel title="Products Explorer" width="70%" height="100%"
				  layout="horizontal">
					<mx:TileList id="tileList" variableRowHeight="false"
					  itemRenderer="components.BookDetailItemRenderer"
					  dataProvider="{xmlData.book}"
					  change="itemSelected(event)" columnCount="2"
					  height="100%" width="100%"/>
				</mx:Panel>
				<mx:Panel width="30%" height="100%" title="Details">
					<mx:Form width="100%" height="100%">
						<mx:FormItem label="Book Name:">
							<mx:Label id="bookName" text="{selectedBook.title}"/>
						</mx:FormItem>
						<mx:FormItem label="ISBN:">
							<mx:Label id="isbnNumber"
							  text="{selectedBook.@ISBN}"/>
						</mx:FormItem>
						<mx:FormItem label="Author:">
							<mx:Label id="authorName">
								<mx:text>{selectedBook.author.firstName}
								  {selectedBook.author.lastName}</mx:text>
							</mx:Label>
						</mx:FormItem>
						<mx:FormItem label="Pages:">
							<mx:Label id="pageNumber"
							  text="{selectedBook.pageCount}"/>
						</mx:FormItem>
						<mx:FormItem label="Price:">
							<mx:Label id="bookPrice"
							  text="{selectedBook.price}"/>
						</mx:FormItem>
						<mx:FormItem label="Description:">
							<mx:Text id="bookDesc"
							  text="{selectedBook.description}" width="200"/>
						</mx:FormItem>
						<mx:FormItem label="Cover Page:">
							<mx:Image width="138"
							  height="146" source="{selectedBook.image}"/>
						</mx:FormItem>
					</mx:Form>
				</mx:Panel>
			</mx:HBox>
			<mx:HBox width="100%" height="30%">
				<mx:Panel width="100%" height="100%" title="Shopping Cart">
					<mx:DataGrid id="dgGrid" dataProvider="{shoppingCart}"
					  height="100%" width="100%" editable="true">
						<mx:columns>
							<mx:DataGridColumn headerText="Book Name"
							  dataField="title" editable="false"/>
							<mx:DataGridColumn headerText="Price"
							  dataField="price" editable="false"/>
							<mx:DataGridColumn headerText="Qty."
							  dataField="quantity" editable="true"/>
						</mx:columns>
					</mx:DataGrid>
					<mx:ControlBar>
						<mx:Button label="Checkout"
						  click="Alert.show(‚Not yet implemented.');"/>
						<mx:Button label="Remove"
						  click="Alert.show(‚Not yet implemented.');"/>
					</mx:ControlBar>
				</mx:Panel>
			</mx:HBox>
		</mx:VBox>
	</mx:Application>

In the code above, we have used the HBox, VBox, and Panel containers to lay out
the main user interface. We have also added a TileList component to display
books using a custom component, that is, BookDetailItemRenderer.mxml as
its itemRenderer. Next, we have added another Panel container to display the
selected book’s details using a Form container. We have used data binding to bind
the selected book’s details with individual labels in the Form container, for example,
text=”{selectedBook.title}”. The selectedBook is an XML object which will be
populated with the selected book’s details when you select an item in the TileList
component using its change event.


		The TileList control displays a number of items laid out in tiles.
		It displays a scroll bar to access all items in the list. You can use its
		direction property to control the direction in which this control
		lays out its children. To know more about the TileList control,
		see Flex 3 language reference at http://livedocs.adobe.com/
		flex/3/langref/mx/controls/TileList.html.

Next, we have created another Panel container to create the shopping cart user
interface and added a DataGrid component to display cart details. The DataGrid
component is using data binding to display information from the shoppingCart
ArrayCollection object, which will contain individual selected book nodes. We will
populate the shopppingCart array in the addToCartH andler() method, which is
the event handler for the addToCart custom event.

In the ActionScript code block, we have defined a method called init() to
initialize the application’s resources and variables. This method is called in the
application tag’s creationComplete event. In this method, we have registered
an event handler for the addToCart event which will be dispatched by the
BookDetailItemRenderer.mxml custom component.

Notice that BookDetailItemRenderer.mxml is acting as an itemRenderer of a
TileList component, so there is no straight way to add an event listener to it.
Therefore, to handle events from the itemRenderer component in its parent, you
need to dispatch an event from custom component by setting its bubble argument
to true. When an event is dispatched with the bubble argument set to true, Flex
searches for event listeners in the bottom to top order—that is, from event target
to root display object. When it finds an appropriate event listener anywhere in its
display hierarchy, it delivers an event to it. This is a simple way to communicate
with your application from itemRenderers.

Next, we are loading XML file using URLLoader and setting its result to the xmlData
XML object, which is used as the dataProvider of the TileList component.

xmlData.book refers to individual <book> node(s) from the XML file.

Now we are ready with our application. Once we compile and execute this
application, you would see the following screen:

You can play with this application. You can try selecting different items from the
TileList control and see how it changes display in the Details panel, and see what
happens when you click on the Add to cart button.

email

«»

Comments

comments