Additional Requirements

You will also need to sign up with Amazon.com to obtain an AWSAccessKey. For more information visit the Amazon Web Services Discussion Forums.
 
Many top brand sites share a common characteristic: they offer great user experiences. An enhanced user experience is not a nice-to-have; it is a must. It is the reason why you or your manager picked Flex in the first place. It is the reason why new applications are built with Flex or that existing applications are rebuilt with Flex. Always remember: making things work is only 50% of the job. The other 50% is making the UI look and feel great.
How can you improve the user experience of an e-commerce application?
  • You can enable users to access the products in their shopping cart without having to switch to the cart view. It would also be convenient for the user to use the drag-and-drop metaphor for putting an item into the cart.
  • You can clearly indicate the view transitions.
  • You can let users search for new items at any time without having to go back to the search screen (for example, when they are in the product details view or in the cart view).
In this article, I introduce KarlStore, a site that offers users a unique buying experience by using Amazon Web Services and Adobe technology to improve on three existing e-commerce metaphors: the cart, the search results, and the product details.
Building an entire e-commerce application is a significant undertaking. In this article I will focus only on searching for items with Amazon Web Services, organizing views, and transitioning between views.
 

 
Pulling data from Amazon Web Services using REST

Amazon Web Services is an infrastructure web services platform for developers of e-commerce web sites. You can access Amazon Web Services using REST or SOAP. The example in this article uses REST.
To get started, create a new project in Flex Builder.
  1. Select File > New > Flex Project.
  2. Type a name for the project, for example, AWSTest (see Figure 1).
  3. Select Web application in the Application type section.
  4. Click Finish.
 
Creating the project
Figure 1. Creating the project
 
Flex Builder will create the project structure for you.
 
Access the web service to retrieve items
Next, create a script block in the main MXML.
In the script block, declare a set of variables that you will use to call the service:
  • The URL to Amazon is the address to the market that you want to retrieve items from.
For example, in the U.S. this value is http://ecs.amazonaws.com/onca/xml; in Canada it is http://ecs.amazonaws.ca/onca/xml; and in the UK it is http://ecs.amazonaws.co.uk/onca/xml.
  • The Operation is the action you want to perform on the Amazon service. In this example, the value is "ItemSearch" because you want to retrieve some items.
  • The Search Index; for example, "Books"
  • The Service; for example, "AWSECommerceService"
  • The AWS Access Key that was provided to you by Amazon when you signed up
  • The Response Groups tell Amazon what information you would like returned for each item. For example, the Images response group returns SmallImage, MediumImage, and LargeImage nodes.
  • The Keywords for the search.
  • The Item Page for paging. You can specify 1, 2, 3, and so on.
The following code show these variable defined for a sample application:
 
[Bindable] private var URL:String = "http://ecs.amazonaws.com/onca/xml?"; [Bindable] private var operation:String = "ItemSearch"; [Bindable] private var SearchIndex:String = "Books"; [Bindable] private var Service:String = "AWSECommerceService"; [Bindable] private var AWSAccessKeyId:String = "********************"; // Enter your AWS Access Key here [Bindable] private var ResponseGroup:String = "Images,ItemAttributes,EditorialReview,Reviews,OfferFull"; [Bindable] private var itemPage:uint = 1; [Bindable] private var totalPages:uint = 1; [Bindable] private var amazonResult:ArrayCollection;
Before the variable declarations, add the imports for the RPC events:
 
import mx.collections.ArrayCollection; import mx.controls.Alert; import mx.rpc.events.FaultEvent; import mx.rpc.events.ResultEvent;
Next, declare the HTTPService component that will be used to fetch items from Amazon:
 
<mx:HTTPService id="amazonRESTSearchService" url="{URL + '&amp;Service=' + Service + '&amp;AWSAccessKeyId=' + AWSAccessKeyId + '&amp;ResponseGroup=' + ResponseGroup + '&amp;Operation=' + operation + '&amp;SearchIndex=' + SearchIndex + '&amp;Keywords=' + searchQueryTextInput.text + '&amp;ItemPage=' + itemPage}" showBusyCursor="true" result="onResult(event)" fault="onFault(event)"> </mx:HTTPService>
Note: The "&amp;" is used in the URL construction of the service to replace the ampersand symbol (&) that separates each variable in a GET query.
As you can see, there are two event handlers for the HTTP Service components: onResult and onFault.The onResult function reads the result of your item search from the event and stores it:
 
private function onResult(event:ResultEvent):void { totalPages = event.result.ItemSearchResponse.Items.TotalPages as uint; amazonResult = event.result.ItemSearchResponse.Items.Item as ArrayCollection; if(!amazonResult) Alert.show("Your query yielded no result"); }
The onFault function simply displays a dialog box with the error message should the request fail:
 
private function onFault(event:FaultEvent):void { Alert.show(event.fault.message); }
To improve the default Alert box display, you can add a CSS Style for the Alert component in the application stylesheet:
 
Alert { font-size: 14; background-color: black; background-alpha: 0.8; border-style: solid; border-thickness: 0; corner-radius: 10; }
You also need a text field for the user to enter the search keywords. Add
 
<mx:TextInput id="searchQueryTextInput" enter="amazonRESTSearchService.send()"> </mx:TextInput>
in your layout.
Lastly, you have to trigger the HTTP service by making a call to it from a button.
 
<mx:Button id="searchQueryButton" label="Search" click="amazonRESTSearchService.send()"> </mx:Button>
To test the application, place a breakpoint in the onResult handler (see Figure 2) and launch the application in debug mode.
Enter some keywords into the text field and click the Search button (see Figure 3).
If the request was successful, you should see an XML response in onResult() with a collection of items (see Figure 4). Sometimes, the request can be successful, but no items are sent back. For example, when the response groups specified for the operations are not valid, the XML reply will contain an error message. If the request was unsuccessful, the onFault() handler will be called and it will display an error message in a dialog box.
 
<mx:ViewStack id="catalogStack" width="100%" height="100%" showEffect="Fade" hideEffect="Fade" creationPolicy="all"> </mx:ViewStack>
The source code with a breakpoint for the onResult event handler
Figure 2: The source code with a breakpoint for the onResult event handler
 
The search text field and button in the application.
Figure 3 : The search text field and button in the application.
 
ItemSearchResponse shown in the Flex Builder 3 Debug perspective.
Figure 4: ItemSearchResponse shown in the Flex Builder 3 Debug perspective.
 
Now that you can pull data from Amazon, you need a way to display it.
To display the data with a TileList, create a <mx:TileList> tag and set its dataProvider to the variable containing the Amazon search results. In this case, this is amazonResult.
 
<!-- Catalog View --> <mx:Box width="80%" height="100%"> <mx:TileList id="catalogTileList" width="100%" height="100%" color="#000000" dragEnabled="true" dataProvider="{amazonResult}" itemRenderer="TileItemRenderer" cornerRadius="10" fontFamily="MyriadWebPro" backgroundColor="white" useRollOver="false" selectionColor="white" verticalScrollPolicy="on"> </mx:TileList> </mx:Box>
Notice the itemRenderer property, which is needed to render each item in the TileList.
Create a new MXML component and name it TileItemRenderer.mxml.
The base node will be the tag Image. To display the Amazon medium image for each item, set the source property to data.LargeImage.URL; data is the property that represents the data for each item rendered.
 

 
Organizing views with the ViewStack component

There are three views you want to display: the search results, the item details, and the cart. In this kind of project, the view stack is an ideal container because it can contain several different child views. You can switch between these views and set transitions between them.
In Flex Builder, create a ViewStack node and give it an id (for example, "catalogStack") for future reference in the code. Set the width and height properties to 100% to use all the space available in the container of the view stack. Setting the showEffect and hideEffect properties to Fade will ensure smooth transitions between views. Lastly, set creationPolicy to all so that all views will be created when the view stack creation is complete (as opposed to being created on the fly when the view is accessed). This ensures that the code will execute without error during initialization if you need to set things up in all three views at startup.
 
<mx:ViewStack id="catalogStack" width="100%" height="100%" showEffect="Fade" hideEffect="Fade" creationPolicy="all"> </mx:ViewStack>
For each view, add a Box or Canvas under the ViewStack tag. Make sure to give each one a unique id so you can reference it in the code later on.
 
<mx:ViewStack id="viewStack" width="100%" height="100%" showEffect="Fade" hideEffect="Fade" creationPolicy="all"> <mx:Box id="searchResultViewBox" width="80%" height="100%"> <mx:TileList id="catalogTileList" width="100%" height="100%" color="#000000" dragEnabled="true" dataProvider="{this.amazonResult}" itemRenderer="TileItemRenderer" cornerRadius="10" fontFamily="MyriadWebPro" backgroundColor="white" useRollOver="false" selectionColor="white" verticalScrollPolicy="on"> </mx:TileList> </mx:Box> <mx:Canvas id="productViewCanvas" width="100%" height="100%" showEffect="Fade" hideEffect="Fade" borderColor="white" cornerRadius="10" borderStyle="solid" borderThickness="2"> </mx:Canvas> <mx:Canvas id="cartViewCanvas" width="100%" height="100%" showEffect="Fade" hideEffect="Fade" borderColor="white" cornerRadius="10" borderStyle="solid" borderThickness="2"> </mx:Canvas> </mx:ViewStack>
A canvas will give you absolute control of the layout. When you position the elements on the screen using the top, left, bottom, and right properties, they will rearrange automatically when the window is resized.
Use the selectedIndex property of the ViewStack to change the view when the user selects an item for details, places an item in the cart, or returns to the search results.
If you leave the code structure as is, then as your application grows the main.mxml will become huge and it will become difficult to understand the structure of your application.
Instead, create an MXML component for each view, and then cut and paste each Box or Canvas underneath the ViewStack in their corresponding component.
SearchResultView.mxml
 
<?xml version="1.0" encoding="utf-8"?> <mx:Box xmlns:mx="http://www.adobe.com/2006/mxml" width="80%" height="100%"> <mx:Script> <![CDATA[ import mx.core.Application; ]]> </mx:Script> <mx:TileList id="catalogTileList" width="100%" height="100%" color="#000000" dragEnabled="true" dataProvider="{Application.application.amazonResult}" itemRenderer="TileItemRenderer" cornerRadius="10" fontFamily="MyriadWebPro" backgroundColor="white" useRollOver="false" selectionColor="white" verticalScrollPolicy="on"> </mx:TileList> </mx:Box>
ProductView.mxml
 
<?xml version="1.0" encoding="utf-8"?> <mx:Canvas xmlns:mx="http://www.adobe.com/2006/mxml" width="100%" height="100%" showEffect="Fade" hideEffect="Fade" borderColor="white" cornerRadius="10" borderStyle="solid" borderThickness="2"> </mx:Canvas>
CartView.mxml
 
<?xml version="1.0" encoding="utf-8"?> <mx:Canvas xmlns:mx="http://www.adobe.com/2006/mxml" width="100%" height="100%" showEffect="Fade" hideEffect="Fade" borderColor="white" cornerRadius="10" borderStyle="solid" borderThickness="2"> </mx:Canvas>
If your SearchResultView.mxml is located in the package com.yourcompany.core.view.searchResult, for example, then you need to add the corresponding XML namespace to the Application tag. Do the same for the product view and for the cart view. Your main.mxml file now looks like this:
 
<?xml version="1.0" encoding="utf-8"?> <mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" xmlns:searchResult="com.yourcompany.core.view.searchResult.*" xmlns:product="com.yourcompany.core.view.product.*" xmlns:cart="com.yourcompany.core.view.cart.*" layout="absolute"> <mx:ViewStack id="viewStack" width="100%" height="100%" showEffect="Fade" hideEffect="Fade" creationPolicy="all"> <searchResult:SearchResultView id="searchResultViewBox" /> <product:ProductView id="productViewCanvas" /> <cart:CartView id="cartViewCanvas" /> </mx:ViewStack> </mx:Application>
Your application code is now well structured and you can proceed to add the components needed for each view.
 

 
Using overlays to signal view changes

It is important to let users of your application know when the view has changed and which view the application has changed to.
To do this using overlays, follow the steps below.
Create a style sheet and name it application-styles.css
In the main MXML file, create a Style tag that points to the application style sheet:
 
<mx:Style source="application-styles.css" />
In the style sheet, add a class for the overlay:
 
.OverlayStyle { background-color: black; background-alpha: 0.5; border-style: solid; border-thickness: 0; corner-radius: 25; }
This style has a semitransparent background with rounded corners and no borders.
Next, create a new MXML component for the overlay. Call it ViewOverlay.mxml.
 
<?xml version="1.0" encoding="utf-8"?> <mx:Box xmlns:mx="http://www.adobe.com/2006/mxml" xmlns:local="*" width="25%" height="35%" visible="false" showEffect="Fade" hideEffect="Fade" mouseEnabled="false" horizontalCenter="0" verticalCenter="0" > </mx:Box>
If your overlay MXML component is located at com.yourcompany.core.view.overlay, for example, then add the following XML namespace to the main MXML:
 
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" xmlns:overlay="com.yourcompany.core.view.overlay.*" layout="absolute" />
Now, instantiate the overlay component, by adding the following at the end of the same file.
 
<overlay:ViewOverlay />
Set the styleName to OverlayStyle, the style class you created earlier.
 
<overlay:ViewOverlay styleName="OverlayStyle" ></overlay:ViewOverlay>
Set the mouseEnabled property to false so the clicks are not intercepted by the overlay and you can still interact with what's behind it.
 
<overlay:ViewOverlay styleName="OverlayStyle" mouseEnabled="false"> </overlay:ViewOverlay>
Since you are using an absolute layout, anything you display at the bottom of the main MXML after the ViewStack will be displayed on top of the ViewStack content. This is called layering and it is similar to the way layers work in Flash CS4. To display the overlay at the center of the screen, set the horizontalCenter and verticalCenter properties to 0.
 
<overlay:ViewOverlay styleName="OverlayStyle" mouseEnabled="false" horizontalCenter="0" verticalCenter="0"> </overlay:ViewOverlay>
The overlay has now been created, but it is still empty.
You have two options to display a symbol within the overlay. You can use a 32-bit PNG bitmap with an alpha channel, which will ensure the symbol anti-aliases against any background, or you can use a vector symbol created with the Flex Component Kit for Flash CS3. The advantage of a vector graphic is that it scales at any size without losing quality. The advantage of the bitmap is that it is faster to render.
A proliferation of vector graphics in your application will impact performance. Too many embedded bitmaps and your application will become too large. You have to find the right balance between bitmap and vector, so your application can be both rich and responsive.
 
Use a bitmap
To use a bitmap, start by creating a 32-bit PNG bitmap with transparency in Photoshop, Fireworks, or Flash CS4.
For more information on creating these bitmaps, see my earlier article.
Save your image as symbol.png. Next, use the Image control to instantiate it:
 
<overlay:ViewOverlay styleName= "OverlayStyle" mouseEnabled="false"> horizontalCenter="0" verticalCenter="0"> <mx:Image id="widescreenImage" source="symbol.png"> </mx:Image> </overlay:ViewOverlay>
The Cart view with the overlay.
Figure 5: The Cart view with the overlay.
 
 
Use a vector graphic in Flash CS4
To use a vector graphic, start by opening the library (press F11 or Ctrl+L).
You can import an existing vector graphic or create a new one.
To import an existing vector graphic in Flash, choose File > Import > Import to Library, then select an Illustrator AI file for import (for example a SVG file or an EPS file saved as an AI file), and click OK to import all layers. Flash creates the graphic symbol.
To create a new graphic using drawing tools in Flash:
  1. Create a new Movieclip
  2. Edit the newly created Movieclip and add the graphic symbol to it
  3. Make sure the graphic is top left aligned to the cross
  4. In the library, select the Movieclip and rename it to Symbol.
  5. In the application menu, choose Commands > Convert Symbol to Flex component (see Figure 6). The component is now ready to be used in Flex.
 
Converting your Flash symbol to a Flex component.
Figure 6: Converting your Flash symbol to a Flex component.
 
  1. Save the FLA file and publish the project (press Shift+F12)
  2. Delete the SWF. Only the SWC file will be used.
  3. Copy the SWC file to the libs folder of your Flex project.
  4. Add the local namespace:
 
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" xmlns:local="*" xmlns:overlay="com.yourcompany.core.view.overlay.*" layout="absolute" />
  1. Instantiate the symbol within the overlay tag:
 
<overlay:ViewOverlay id="viewIcon" styleName= "OverlayStyle" mouseEnabled="false" hideEffect="Fade" horizontalCenter="0" verticalCenter="0"> <local:Symbol/> </overlay:ViewOverlay>
 
Display the overlay
Notice that the visible property in ViewOverlay.mxml is set to false. It is up to you to control how the overlay is displayed and when.
First, give the viewIcon ID to the ViewOverlay component instance:
 
<overlay:ViewOverlay id="viewIcon" styleName="OverlayStyle" mouseEnabled="false" horizontalCenter="0" verticalCenter="0"> </overlay:ViewOverlay>
  1. Within the <Script> tag of the Application, import the Timer and TimerEvent classes:
 
import flash.utils.Timer; import flash.events.TimerEvent;
  1. Create a timer variable:
 
private var viewTimer:Timer;
  1. Add the line creationComplete="onCreationComplete()" to the Application tag.
    In the onCreationComplete method, create a Timer instance and add an event listener for it:
 
private function onCreationComplete():void { viewTimer = new Timer(1000, 0); viewTimer.addEventListener(TimerEvent.TIMER, onViewImageDelete); }
  1. Write an event handler for the timer that will hide the overlay and reset the timer:
 
private function onViewImageDelete(event:TimerEvent):void { this.viewIcon.visible = false; this.viewTimer.reset(); }
  1. Whenever the user changes view, trigger the display of the overlay and start the timer:
 
private function viewChange():void { if(viewTimer) { viewIcon.visible = true; viewTimer.start(); } }
  1. Define the viewChange() method as the event handler for the ViewStack change event:
 
<mx:ViewStack id="catalogStack" change="viewChange()" width="100%" height="90%" bottom="0" left="0" showEffect="Fade" hideEffect="Fade" creationPolicy="all">
The final script should look like this:
 
<mx:Script> <![CDATA[ import flash.utils.Timer; import flash.events.TimerEvent; private var viewTimer:Timer; private function onCreationComplete():void { viewTimer = new Timer(1000, 0); viewTimer.addEventListener(TimerEvent.TIMER, onViewImageDelete); } private function onViewImageDelete(event:TimerEvent):void { viewIcon.visible = false; viewTimer.reset(); } private function viewChange():void { if(viewTimer) { viewIcon.visible = true; viewTimer.start(); } } ]]> </mx:Script>
Now, when the view changes the user will see the overlay appear immediately and then disappear with a fade effect after one second.

 
Where to go from here

In this article, you learned how to retrieve data from Amazon Web Services using REST. You also learned how to improve the overall buying experience, which is likely one of the reasons you decided to use Flex in the first place. For more guidance on user experience design, see the Flex Interface Guide.
Instead of using REST, you could access Amazon Web Services with the Flex Builder WSDL wizard by choosing Data > Import Web Service (WSDL) and use the following web service address: http://soap.amazon.com/schemas2/AmazonWebServices.wsdl
You may want to try some of the other available Amazon Web Service operations beyond simply searching for items. You can add an item to your cart, modify the cart, clear the cart, and so on. For more details, see the Amazon Web Service Developer Documentation.
You can also use the Cairngorm architectural framework to give more structure to your application. See Steven Webster's series of Cairngorm articles, which are based on the Flex Store.