How to use Flex to get Music Info from Amazon Web Services
|
Below you can see the application that we are going to create. All you have to do is type in your search, hit the "Search" button, and your off getting music information. Once you have some results you can select one in the datagrid to get more information about it. This extra information includes the cover art image, album information, and editorial reviews. I apologize for the fact that the application is so narrow, the blog is only about 500px wide. You're welcome to create a wider version if you want |
The first thing we're going to do is setup the interface for the application. The code below is the basic application setup with a couple of items to note. At the top of the application the search textbox and button are added, and I also added a small note in the top right hand corner. There is also a datagrid inside the application with four columns - don't mind them just yet. A canvas is added to the application which will hold the extra information about the specific album we are looking at. You will also notice in the code below that I added a gradient property to the
<mx:Application> tag to get the nice gradient on the app. You can read more about this property at the Adobe Livedocs here. I also added some alpha transparency to the canvas so the gradient would show up a little on it.<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"
layout="absolute" width="530" height="671"
backgroundGradientColors="[0x5E8499, 0xFFFFFF]"
viewSourceURL="/files/AmazonMusicTutorial.mxml">
<mx:TextInput id="txtKeywords" x="99" y="10" width="181"/>
<mx:Label x="10" y="12" text="Search Music:"/>
<mx:Button x="288" y="10" label="Search"/>
<mx:DataGrid x="10" y="38" width="510" height="300"
id="dgItems" fontSize="9">
<mx:columns>
<mx:DataGridColumn headerText="Artist" width="100"/>
<mx:DataGridColumn headerText="Title"/>
<mx:DataGridColumn headerText="Label" width="100"/>
<mx:DataGridColumn headerText="ASIN" width="90"/>
</mx:columns>
</mx:DataGrid>
<mx:Canvas x="10" y="346" width="510" height="313"
borderStyle="solid" backgroundColor="#EFEFEF"
backgroundAlpha=".5">
</mx:Canvas>
</mx:Application>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"
layout="absolute" width="530" height="671"
backgroundGradientColors="[0x5E8499, 0xFFFFFF]"
viewSourceURL="/files/AmazonMusicTutorial.mxml">
<mx:TextInput id="txtKeywords" x="99" y="10" width="181"/>
<mx:Label x="10" y="12" text="Search Music:"/>
<mx:Button x="288" y="10" label="Search"/>
<mx:DataGrid x="10" y="38" width="510" height="300"
id="dgItems" fontSize="9">
<mx:columns>
<mx:DataGridColumn headerText="Artist" width="100"/>
<mx:DataGridColumn headerText="Title"/>
<mx:DataGridColumn headerText="Label" width="100"/>
<mx:DataGridColumn headerText="ASIN" width="90"/>
</mx:columns>
</mx:DataGrid>
<mx:Canvas x="10" y="346" width="510" height="313"
borderStyle="solid" backgroundColor="#EFEFEF"
backgroundAlpha=".5">
</mx:Canvas>
</mx:Application>
To finish off the user interface lets add the components for the extended information. There aren't any here that are funky so nothing should look off kilter. The only customized item is the textarea used for displaying the reviews which is changed to be uneditable. Besides that element we have a bunch of labels to show data we bring in and an
<mx:Image> tag to show the cover art. The code is added right below our opening <mx:Canvas> tagw.
<mx:Image id="imgAlbumCover" x="10" y="53"
width="250" height="250" scaleContent="true"/>
<mx:Label x="12" y="14" width="486" fontSize="11"
fontWeight="bold" id="lblArtistTitle" />
<mx:Label x="268" y="41" text="Orginal Release Date:"
fontSize="9"/>
<mx:Label x="392" y="41" id="lblReleaseDate"
fontSize="9" width="106"/>
<mx:Label x="268" y="67" text="Number of Discs:"
fontSize="9"/>
<mx:Label x="368" y="67" id="lblNumberDiscs"
fontSize="9" width="130"/>
<mx:Label x="268" y="93" text="Label:" fontSize="9"/>
<mx:Label x="312" y="93" id="lblLabel" fontSize="9"
width="186"/>
<mx:Label x="268" y="119" text="ASIN:" fontSize="9"/>
<mx:Label x="313" y="119" id="lblASIN" fontSize="9"
width="185"/>
<mx:Label x="268" y="145" text="Editorial Reviews:"
fontSize="9"/>
<mx:TextArea id="txtReview" x="268" y="171"
width="230" height="132" backgroundAlpha="0.0"
editable="false" wordWrap="true"/>
width="250" height="250" scaleContent="true"/>
<mx:Label x="12" y="14" width="486" fontSize="11"
fontWeight="bold" id="lblArtistTitle" />
<mx:Label x="268" y="41" text="Orginal Release Date:"
fontSize="9"/>
<mx:Label x="392" y="41" id="lblReleaseDate"
fontSize="9" width="106"/>
<mx:Label x="268" y="67" text="Number of Discs:"
fontSize="9"/>
<mx:Label x="368" y="67" id="lblNumberDiscs"
fontSize="9" width="130"/>
<mx:Label x="268" y="93" text="Label:" fontSize="9"/>
<mx:Label x="312" y="93" id="lblLabel" fontSize="9"
width="186"/>
<mx:Label x="268" y="119" text="ASIN:" fontSize="9"/>
<mx:Label x="313" y="119" id="lblASIN" fontSize="9"
width="185"/>
<mx:Label x="268" y="145" text="Editorial Reviews:"
fontSize="9"/>
<mx:TextArea id="txtReview" x="268" y="171"
width="230" height="132" backgroundAlpha="0.0"
editable="false" wordWrap="true"/>
Next thing we are going to do is setup our web service from Amazon, but we have to take care of some janitorial issues first. To be able to use Amazon Web Services we need to get an AWS Access Id. The id is free, all you have to do to get one is sign up at the Web Services website. You can create an account right here. Once you have signed up you can get your AWS Access Id by going to right-hand side of the home page and to "Your Web Services Account" and to "AWS Access Identifiers". This should take you to the page where you can get your access id. The image below shows the location of the "AWS Access Identifiers" link.
Ok, let's get back to the flex code. What we are going to do now is setup the webservice using a
<mx:WebService> tag. This tag will go right under our opening application tag. There are a couple of properties we need to make sure we set for this. These include the id, which we use to identify the service later, the wsdl (or Web Service Definition Language), which describes the service and how it can used (and in this case is a link given to us by Amazon), showBusyCursor, and fault event. The fault event tells flex what to do if the service doesn't work.Inside our
<mx:WebService> tag we add an operation (<mx:operation>), which is basically a function that the service has available to call. For this tag we set two properties, which are both very important. The first is name, which is the qualified name of the operation - in this case "ItemSearch" (this is defined by Amazon). The second is resultFormat, which we set to "object". This tells flex to create objects from the xml data we get back from Amazon. This makes it really easy for us to use the data coming back. The last thing we need to do is setup what parameters are sent to the operation.To set the parameters we use a
<mx:request> tag inside the <mx:operation> tag. We then set a few parameters. The first one is the "AWSAccessKeyId", which is given to us by Amazon. The next couple of parameters we send as a "Shared" object by wrapping them in a <Shared> tag. These are the parameters we actually use for the search query. We use "Keywords", "SearchIndex", and "Count", which are pretty self explanatory, but there are tons of parameters that can be sent with this operation. The documentation from Amazon on their operation is pretty good and can be found here - under ECS API Reference -> ECS Operations. So all of this results in the following code which is added right below the opening application tag.<mx:WebService id="AmazonSearch"
wsdl="{'http://webservices.amazon.com/'
+ 'AWSECommerceService/AWSECommerceService.wsdl'}"
showBusyCursor="true"
fault="Alert.show(event.fault.faultString)">
<mx:operation name="ItemSearch" resultFormat="object">
<mx:request>
<AWSAccessKeyId>############</AWSAccessKeyId>
<Shared>
<Keywords>{keywords}</Keywords>
<SearchIndex>Music</SearchIndex>
<Count>20</Count>
</Shared>
</mx:request>
</mx:operation>
</mx:WebService>
wsdl="{'http://webservices.amazon.com/'
+ 'AWSECommerceService/AWSECommerceService.wsdl'}"
showBusyCursor="true"
fault="Alert.show(event.fault.faultString)">
<mx:operation name="ItemSearch" resultFormat="object">
<mx:request>
<AWSAccessKeyId>############</AWSAccessKeyId>
<Shared>
<Keywords>{keywords}</Keywords>
<SearchIndex>Music</SearchIndex>
<Count>20</Count>
</Shared>
</mx:request>
</mx:operation>
</mx:WebService>
But wait! It won't even compile yet. You will see some compile errors for the missing variable we are using, "keywords", and something about Alert. We need to add some script to import the Alert and create a variable for our keywords. We add a
<mx:Script> tag right above our <mx:WebService> tag to do this. The following is the script tag we are creating.<mx:Script>
<![CDATA[
import mx.controls.Alert;
[Bindable]
private var keywords:String = "";
]]>
</mx:Script>
<![CDATA[
import mx.controls.Alert;
[Bindable]
private var keywords:String = "";
]]>
</mx:Script>
Because we want to actually use the keywords from our text input we use a
<mx:Binding> tag to bind our textInput text to our keywords variable. If you need more information on the binding tag check out this earlier tutorial. Our new binding tag goes directly under the closing script tag.<mx:Binding source="txtKeywords.text" destination="keywords" />
Now we can compile the app but we have no interaction with our new service yet. To do this we need two pieces: some way to send the service a request and a way to handle the data when it comes back. The first is accomplished pretty easily. We do this by adding a click event to our button which will call a function to send an operation request. Our new button code is below.
<mx:Button x="288" y="10" label="Search" click="sendRequest()"/>
Adding the function "sendRequest" to our actionscript will now let us call our service. Inside our function we do two things. The first is escape our keywords because amazon needs whitespaces to be changed to "%20". The second is calling the service request. This is accomplished by simply calling the send command on our operation. This function goes inside our script tag.
private function sendRequest():void
{
keywords = escape(keywords);
AmazonSearch.ItemSearch.send();
}
{
keywords = escape(keywords);
AmazonSearch.ItemSearch.send();
}
To process the information coming back we need to add a result event to our operation. This event will call a function with a
ResultEvent object passed in as a parameter, which will hold our data. I called this function "showItemSearchResult". Our operation tag looks like the following:<mx:operation name="ItemSearch" resultFormat="object"
result="showItemSearchResult(event)">
result="showItemSearchResult(event)">
Again, we add this function to our script tag, but we need to add a couple of supporting imports and a variables. The var we create is an ArrayCollection named "itemResults", which we are going to use to put the data into our datagrid. Then in our function we can set our new "itemResults" equal to the array of items returned. To actually find out where in the result object we get the item array you can look through the Amazon documentation. I actually spent some time in the debugger checking out the contents of the
res.result object, which helped me determine how to get all the information we need. So our new script block is updated to the following.<mx:Script>
<![CDATA[
import mx.collections.ArrayCollection;
import mx.rpc.events.ResultEvent;
import mx.controls.Alert;
[Bindable]
private var keywords:String = "";
[Bindable]
private var itemResults:ArrayCollection;
private function sendRequest():void
{
keywords = escape(keywords);
AmazonSearch.ItemSearch.send();
}
private function showItemSearchResult(res:ResultEvent):void
{
itemResults = res.result.Items.Item;
}
]]>
</mx:Script>
<![CDATA[
import mx.collections.ArrayCollection;
import mx.rpc.events.ResultEvent;
import mx.controls.Alert;
[Bindable]
private var keywords:String = "";
[Bindable]
private var itemResults:ArrayCollection;
private function sendRequest():void
{
keywords = escape(keywords);
AmazonSearch.ItemSearch.send();
}
private function showItemSearchResult(res:ResultEvent):void
{
itemResults = res.result.Items.Item;
}
]]>
</mx:Script>
With a little work on our datagrid we will be able to display our results from Amazon. The first thing we are going to do is bind our arrayCollection to the dataprovider of the datagrid. We do this inside the
<mx:DataGrid> tag. The next thing we need to do is bind the set of info that is going to be displayed in our columns. We are going to use the labelFunction property of the DataGridColumns. Basically this calls a function that returns a string (which will be the data shown). For more information on this look for a upcoming tutorial on advanced dataGrid techniques. The last column, "ASIN", which is Amazon's product Id number, will just be a datafield. Our Datagrid object now looks like:<mx:DataGrid x="10" y="38" width="510" height="300"
id="dgItems" fontSize="9" dataProvider="{itemResults}">
<mx:columns>
<mx:DataGridColumn headerText="Artist"
labelFunction="getItemArtist" width="100" />
<mx:DataGridColumn headerText="Title"
labelFunction="getItemTitle" />
<mx:DataGridColumn headerText="Label"
labelFunction="getItemLabel" width="100" />
<mx:DataGridColumn headerText="ASIN"
dataField="ASIN" width="90" />
</mx:columns>
</mx:DataGrid>
id="dgItems" fontSize="9" dataProvider="{itemResults}">
<mx:columns>
<mx:DataGridColumn headerText="Artist"
labelFunction="getItemArtist" width="100" />
<mx:DataGridColumn headerText="Title"
labelFunction="getItemTitle" />
<mx:DataGridColumn headerText="Label"
labelFunction="getItemLabel" width="100" />
<mx:DataGridColumn headerText="ASIN"
dataField="ASIN" width="90" />
</mx:columns>
</mx:DataGrid>
Wallah! We can now search Amazon for music albums using a keyword search and display the results in our datagrid. The next step is to get the extended data when we select a particular album in the list. This entails pretty much the same kinds of things we did before but we process the data a little differently. The first thing we do is add another operation to the our WebService which will be used to get detailed information about the album we have selected.
This new operation is named "ItemLookup" and we set it up to call a function named "showItemLookupResults" when the results come back. The parameters are added the same way as before but they are a little different. We still pass our access key but our
Shared parameters need to be changed. The first is the amazon item id of the current item we have selected (don't worry, we will set this up in a moment). The second parameter we pass is ResponseGroup. This parameter defines the information that will be passed back to us. Again this information can be found in the Amazon documentation. The following code goes inside in the <mx:WebService> tag after the first operation.<mx:operation name="ItemLookup" resultFormat="object"
result="showItemLookupResults(event)">
<mx:request>
<AWSAccessKeyId>#############</AWSAccessKeyId>
<Shared>
<ItemId>{currentItem.ASIN}</ItemId>
<ResponseGroup>
ItemAttributes,Images,Tracks,EditorialReview
</ResponseGroup>
</Shared>
</mx:request>
</mx:operation>
result="showItemLookupResults(event)">
<mx:request>
<AWSAccessKeyId>#############</AWSAccessKeyId>
<Shared>
<ItemId>{currentItem.ASIN}</ItemId>
<ResponseGroup>
ItemAttributes,Images,Tracks,EditorialReview
</ResponseGroup>
</Shared>
</mx:request>
</mx:operation>
Next we do a little actionscript work. We setup three new variables, the one referred to in the new operation and two that are used for the details we process from the web service. We also create our function to receive and process the information we get back from "ItemLookup". We will fill this out in just a second. The new code we add to our script tag is below.
[Bindable]
private var currentItem:Object;
[Bindable]
private var currentItemDetails:Object;
[Bindable]
private var currentItemAttr:Object;
private function
showItemLookupResults(res:ResultEvent):void
{
}
private var currentItem:Object;
[Bindable]
private var currentItemDetails:Object;
[Bindable]
private var currentItemAttr:Object;
private function
showItemLookupResults(res:ResultEvent):void
{
}
When we get the data back we process it using our "showItemLookupResults" function. To start off we take the item object we get back and set it to "currentItemDetails" (one of the variables we created), next we set the ItemAttributes of this item equal to another one of the variables we created, "currentItemAttr". Really the only reason this var is created is to save space on the blog so the code will fit nicely. We will bind these variables to the UI in a minute.
The next thing done in this function is setting the title. I first check to see if the artist is filled in, and if not I use the string "Unknown" just to make it look nice.Then I set the text of that label to the title string. The editorial reviews take a little bit more work. I first check to see if the incoming review(s) is an ArrayCollection. If there are multiple reviews it is, and if so append each of the reviews together to make a long review. If it is isn't multiple reviews we check to see if there are any reviews at all (if property for reviews is available). If not we just print out "No Reviews Available", otherwise we print out the review(s). The final thing done is setting the image component source to highest quality image or to nothing if they are not available. The following code replaces our recently created "showItemLookupResults" function.
private function
showItemLookupResults(res:ResultEvent):void
{
currentItemDetails = res.result.Items.Item;
currentItemAttr = currentItemDetails.ItemAttributes;
var titleString:String = "";
if(!currentItemDetails.ItemAttributes.Artist) {
titleString = "Unknown - " +
currentItemDetails.ItemAttributes.Title;
} else {
titleString = currentItemDetails.ItemAttributes.Artist
+ " - " + currentItemDetails.ItemAttributes.Title;
}
lblArtistTitle.text = titleString;
var reviewString:String = "";
if(currentItemDetails.EditorialReviews
is ArrayCollection)
{
for each (var review:Object in
currentItemDetails.EditorialReviews)
{
reviewString += (review.Content + "<br /><br />");
}
} else {
if(!currentItemDetails.EditorialReviews)
reviewString = "No Reviews Available"
else
reviewString = currentItemDetails.EditorialReviews.Content;
}
txtReview.htmlText = reviewString;
var imgUrl:String = "";
if(currentItemDetails.LargeImage)
imgUrl = currentItemDetails.LargeImage.URL;
else if (currentItemDetails.MediumImage)
imgUrl = currentItemDetails.MediumImage.URL;
else if (currentItemDetails.SmallImage)
imgUrl = currentItemDetails.SmallImage.URL;
imgAlbumCover.source = imgUrl;
}
showItemLookupResults(res:ResultEvent):void
{
currentItemDetails = res.result.Items.Item;
currentItemAttr = currentItemDetails.ItemAttributes;
var titleString:String = "";
if(!currentItemDetails.ItemAttributes.Artist) {
titleString = "Unknown - " +
currentItemDetails.ItemAttributes.Title;
} else {
titleString = currentItemDetails.ItemAttributes.Artist
+ " - " + currentItemDetails.ItemAttributes.Title;
}
lblArtistTitle.text = titleString;
var reviewString:String = "";
if(currentItemDetails.EditorialReviews
is ArrayCollection)
{
for each (var review:Object in
currentItemDetails.EditorialReviews)
{
reviewString += (review.Content + "<br /><br />");
}
} else {
if(!currentItemDetails.EditorialReviews)
reviewString = "No Reviews Available"
else
reviewString = currentItemDetails.EditorialReviews.Content;
}
txtReview.htmlText = reviewString;
var imgUrl:String = "";
if(currentItemDetails.LargeImage)
imgUrl = currentItemDetails.LargeImage.URL;
else if (currentItemDetails.MediumImage)
imgUrl = currentItemDetails.MediumImage.URL;
else if (currentItemDetails.SmallImage)
imgUrl = currentItemDetails.SmallImage.URL;
imgAlbumCover.source = imgUrl;
}
For a quick task we add a new binding statement for our datagrid and the currently selected Item. This is a simple
<mx:Binding> tag where the source is the selected item in our datagrid and the destination is our variable. So our new binding code goes right underneath the old one.<mx:Binding source="dgItems.selectedItem"
destination="currentItem" />
destination="currentItem" />
To finish off our data bindings we bind the text of several labels to properties of our "currentItemAttr" variable. These include the original release date, number of discs, and label. For the ASIN we bind to our "currentItemDetails". We replace the following labels.
<mx:Label x="392" y="41" id="lblReleaseDate"
fontSize="9" width="106"
text="{currentItemAttr.OriginalReleaseDate}"/>
fontSize="9" width="106"
text="{currentItemAttr.OriginalReleaseDate}"/>
<mx:Label x="368" y="67" id="lblNumberDiscs"
fontSize="9" width="130"
text="{currentItemAttr.NumberOfDiscs}"/>
fontSize="9" width="130"
text="{currentItemAttr.NumberOfDiscs}"/>
<mx:Label x="312" y="93" id="lblLabel" fontSize="9"
width="186" text="{currentItemAttr.Label}"/>
width="186" text="{currentItemAttr.Label}"/>
<mx:Label x="313" y="119" id="lblASIN" fontSize="9"
width="185" text="{currentItemDetails.ASIN}"/>
width="185" text="{currentItemDetails.ASIN}"/>
And the very last thing we need to do is actually send the request for the data. We are going to do this using the change event of the datagrid. This is done by simply adding the event to the component and making it send the service request directly from the event property. Now our
<mx:DataGrid> tag is in its final state:<mx:DataGrid x="10" y="38" width="510" height="300"
id="dgItems" fontSize="9" dataProvider="{itemResults}"
change="AmazonSearch.ItemLookup.send()">
id="dgItems" fontSize="9" dataProvider="{itemResults}"
change="AmazonSearch.ItemLookup.send()">
Boom Shakalaka! We have a fully working music search using Amazon's great Web Services. I also added a little code to initialize the app on this page, but it isn't important for the tutorial. Here is the full source for the application.
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"
layout="absolute" width="530" height="671"
backgroundGradientColors="[0x5E8499, 0xFFFFFF]"
viewSourceURL="/files/AmazonMusicTutorial.mxml">
<mx:Script>
<![CDATA[
import mx.collections.ArrayCollection;
import mx.rpc.events.ResultEvent;
import mx.controls.Alert;
[Bindable]
private var keywords:String = "";
[Bindable]
private var itemResults:ArrayCollection;
[Bindable]
private var currentItem:Object;
[Bindable]
private var currentItemDetails:Object;
[Bindable]
private var currentItemAttr:Object;
private function sendRequest():void
{
keywords = escape(keywords);
AmazonSearch.ItemSearch.send();
}
private function
showItemSearchResult(res:ResultEvent):void
{
itemResults = res.result.Items.Item;
}
private function getItemArtist(item:Object,
column:DataGridColumn):String
{
return item.ItemAttributes.Artist;
}
private function getItemTitle(item:Object,
column:DataGridColumn):String
{
return item.ItemAttributes.Title;
}
private function getItemLabel(item:Object,
column:DataGridColumn):String
{
return item.ItemAttributes.Manufacturer;
}
private function
showItemLookupResults(res:ResultEvent):void
{
currentItemDetails = res.result.Items.Item;
currentItemAttr = currentItemDetails.ItemAttributes;
var titleString:String = "";
if(!currentItemDetails.ItemAttributes.Artist) {
titleString = "Unknown - " +
currentItemDetails.ItemAttributes.Title;
} else {
titleString = currentItemDetails.ItemAttributes.Artist
+ " - " + currentItemDetails.ItemAttributes.Title;
}
lblArtistTitle.text = titleString;
var reviewString:String = "";
if(currentItemDetails.EditorialReviews
is ArrayCollection)
{
for each (var review:Object in
currentItemDetails.EditorialReviews)
{
reviewString += (review.Content + "<br /><br />");
}
} else {
if(!currentItemDetails.EditorialReviews)
reviewString = "No Reviews Available"
else
reviewString =
currentItemDetails.EditorialReviews.Content;
}
txtReview.htmlText = reviewString;
var imgUrl:String = "";
if(currentItemDetails.LargeImage)
imgUrl = currentItemDetails.LargeImage.URL;
else if (currentItemDetails.MediumImage)
imgUrl = currentItemDetails.MediumImage.URL;
else if (currentItemDetails.SmallImage)
imgUrl = currentItemDetails.SmallImage.URL;
imgAlbumCover.source = imgUrl;
}
]]>
</mx:Script>
<mx:Binding source="txtKeywords.text"
destination="keywords" />
<mx:Binding source="dgItems.selectedItem"
destination="currentItem" />
<mx:WebService id="AmazonSearch"
wsdl="{'http://webservices.amazon.com/'
+ 'AWSECommerceService/AWSECommerceService.wsdl'}"
showBusyCursor="true"
fault="Alert.show(event.fault.faultString)">
<mx:operation name="ItemSearch" resultFormat="object"
result="showItemSearchResult(event)">
<mx:request>
<AWSAccessKeyId>################</AWSAccessKeyId>
<Shared>
<Keywords>{keywords}</Keywords>
<SearchIndex>Music</SearchIndex>
<Count>20</Count>
</Shared>
</mx:request>
</mx:operation>
<mx:operation name="ItemLookup" resultFormat="object"
result="showItemLookupResults(event)">
<mx:request>
<AWSAccessKeyId>#################</AWSAccessKeyId>
<Shared>
<ItemId>{currentItem.ASIN}</ItemId>
<ResponseGroup>
ItemAttributes,Images,Tracks,EditorialReview
</ResponseGroup>
</Shared>
</mx:request>
</mx:operation>
</mx:WebService>
<mx:TextInput id="txtKeywords" x="99" y="10" width="181"/>
<mx:Label x="10" y="12" text="Search Music:"/>
<mx:Button x="288" y="10" label="Search"
click="sendRequest()"/>
<mx:DataGrid x="10" y="38" width="510" height="300"
id="dgItems" fontSize="9" dataProvider="{itemResults}"
change="AmazonSearch.ItemLookup.send()">
<mx:columns>
<mx:DataGridColumn headerText="Artist"
labelFunction="getItemArtist" width="100" />
<mx:DataGridColumn headerText="Title"
labelFunction="getItemTitle" />
<mx:DataGridColumn headerText="Label"
labelFunction="getItemLabel" width="100" />
<mx:DataGridColumn headerText="ASIN"
dataField="ASIN" width="90" />
</mx:columns>
</mx:DataGrid>
<mx:Canvas x="10" y="346" width="510" height="313"
borderStyle="solid" backgroundColor="#EFEFEF"
backgroundAlpha=".5">
<mx:Image id="imgAlbumCover" x="10" y="53"
width="250" height="250" scaleContent="true"/>
<mx:Label x="12" y="14" width="486" fontSize="11"
fontWeight="bold" id="lblArtistTitle" />
<mx:Label x="268" y="41" text="Orginal Release Date:"
fontSize="9"/>
<mx:Label x="392" y="41" id="lblReleaseDate"
fontSize="9" width="106"
text="{currentItemAttr.OriginalReleaseDate}"/>
<mx:Label x="268" y="67" text="Number of Discs:"
fontSize="9"/>
<mx:Label x="368" y="67" id="lblNumberDiscs"
fontSize="9" width="130"
text="{currentItemAttr.NumberOfDiscs}"/>
<mx:Label x="268" y="93" text="Label:" fontSize="9"/>
<mx:Label x="312" y="93" id="lblLabel" fontSize="9"
width="186" text="{currentItemAttr.Label}"/>
<mx:Label x="268" y="119" text="ASIN:" fontSize="9"/>
<mx:Label x="313" y="119" id="lblASIN" fontSize="9"
width="185" text="{currentItemDetails.ASIN}"/>
<mx:Label x="268" y="145" text="Editorial Reviews:"
fontSize="9"/>
<mx:TextArea id="txtReview" x="268" y="171"
width="230" height="132" backgroundAlpha="0.0"
editable="false" wordWrap="true"/>
</mx:Canvas>
</mx:Application>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"
layout="absolute" width="530" height="671"
backgroundGradientColors="[0x5E8499, 0xFFFFFF]"
viewSourceURL="/files/AmazonMusicTutorial.mxml">
<mx:Script>
<![CDATA[
import mx.collections.ArrayCollection;
import mx.rpc.events.ResultEvent;
import mx.controls.Alert;
[Bindable]
private var keywords:String = "";
[Bindable]
private var itemResults:ArrayCollection;
[Bindable]
private var currentItem:Object;
[Bindable]
private var currentItemDetails:Object;
[Bindable]
private var currentItemAttr:Object;
private function sendRequest():void
{
keywords = escape(keywords);
AmazonSearch.ItemSearch.send();
}
private function
showItemSearchResult(res:ResultEvent):void
{
itemResults = res.result.Items.Item;
}
private function getItemArtist(item:Object,
column:DataGridColumn):String
{
return item.ItemAttributes.Artist;
}
private function getItemTitle(item:Object,
column:DataGridColumn):String
{
return item.ItemAttributes.Title;
}
private function getItemLabel(item:Object,
column:DataGridColumn):String
{
return item.ItemAttributes.Manufacturer;
}
private function
showItemLookupResults(res:ResultEvent):void
{
currentItemDetails = res.result.Items.Item;
currentItemAttr = currentItemDetails.ItemAttributes;
var titleString:String = "";
if(!currentItemDetails.ItemAttributes.Artist) {
titleString = "Unknown - " +
currentItemDetails.ItemAttributes.Title;
} else {
titleString = currentItemDetails.ItemAttributes.Artist
+ " - " + currentItemDetails.ItemAttributes.Title;
}
lblArtistTitle.text = titleString;
var reviewString:String = "";
if(currentItemDetails.EditorialReviews
is ArrayCollection)
{
for each (var review:Object in
currentItemDetails.EditorialReviews)
{
reviewString += (review.Content + "<br /><br />");
}
} else {
if(!currentItemDetails.EditorialReviews)
reviewString = "No Reviews Available"
else
reviewString =
currentItemDetails.EditorialReviews.Content;
}
txtReview.htmlText = reviewString;
var imgUrl:String = "";
if(currentItemDetails.LargeImage)
imgUrl = currentItemDetails.LargeImage.URL;
else if (currentItemDetails.MediumImage)
imgUrl = currentItemDetails.MediumImage.URL;
else if (currentItemDetails.SmallImage)
imgUrl = currentItemDetails.SmallImage.URL;
imgAlbumCover.source = imgUrl;
}
]]>
</mx:Script>
<mx:Binding source="txtKeywords.text"
destination="keywords" />
<mx:Binding source="dgItems.selectedItem"
destination="currentItem" />
<mx:WebService id="AmazonSearch"
wsdl="{'http://webservices.amazon.com/'
+ 'AWSECommerceService/AWSECommerceService.wsdl'}"
showBusyCursor="true"
fault="Alert.show(event.fault.faultString)">
<mx:operation name="ItemSearch" resultFormat="object"
result="showItemSearchResult(event)">
<mx:request>
<AWSAccessKeyId>################</AWSAccessKeyId>
<Shared>
<Keywords>{keywords}</Keywords>
<SearchIndex>Music</SearchIndex>
<Count>20</Count>
</Shared>
</mx:request>
</mx:operation>
<mx:operation name="ItemLookup" resultFormat="object"
result="showItemLookupResults(event)">
<mx:request>
<AWSAccessKeyId>#################</AWSAccessKeyId>
<Shared>
<ItemId>{currentItem.ASIN}</ItemId>
<ResponseGroup>
ItemAttributes,Images,Tracks,EditorialReview
</ResponseGroup>
</Shared>
</mx:request>
</mx:operation>
</mx:WebService>
<mx:TextInput id="txtKeywords" x="99" y="10" width="181"/>
<mx:Label x="10" y="12" text="Search Music:"/>
<mx:Button x="288" y="10" label="Search"
click="sendRequest()"/>
<mx:DataGrid x="10" y="38" width="510" height="300"
id="dgItems" fontSize="9" dataProvider="{itemResults}"
change="AmazonSearch.ItemLookup.send()">
<mx:columns>
<mx:DataGridColumn headerText="Artist"
labelFunction="getItemArtist" width="100" />
<mx:DataGridColumn headerText="Title"
labelFunction="getItemTitle" />
<mx:DataGridColumn headerText="Label"
labelFunction="getItemLabel" width="100" />
<mx:DataGridColumn headerText="ASIN"
dataField="ASIN" width="90" />
</mx:columns>
</mx:DataGrid>
<mx:Canvas x="10" y="346" width="510" height="313"
borderStyle="solid" backgroundColor="#EFEFEF"
backgroundAlpha=".5">
<mx:Image id="imgAlbumCover" x="10" y="53"
width="250" height="250" scaleContent="true"/>
<mx:Label x="12" y="14" width="486" fontSize="11"
fontWeight="bold" id="lblArtistTitle" />
<mx:Label x="268" y="41" text="Orginal Release Date:"
fontSize="9"/>
<mx:Label x="392" y="41" id="lblReleaseDate"
fontSize="9" width="106"
text="{currentItemAttr.OriginalReleaseDate}"/>
<mx:Label x="268" y="67" text="Number of Discs:"
fontSize="9"/>
<mx:Label x="368" y="67" id="lblNumberDiscs"
fontSize="9" width="130"
text="{currentItemAttr.NumberOfDiscs}"/>
<mx:Label x="268" y="93" text="Label:" fontSize="9"/>
<mx:Label x="312" y="93" id="lblLabel" fontSize="9"
width="186" text="{currentItemAttr.Label}"/>
<mx:Label x="268" y="119" text="ASIN:" fontSize="9"/>
<mx:Label x="313" y="119" id="lblASIN" fontSize="9"
width="185" text="{currentItemDetails.ASIN}"/>
<mx:Label x="268" y="145" text="Editorial Reviews:"
fontSize="9"/>
<mx:TextArea id="txtReview" x="268" y="171"
width="230" height="132" backgroundAlpha="0.0"
editable="false" wordWrap="true"/>
</mx:Canvas>
</mx:Application>
I hope you have found this tutorial helpful and interesting. As an added bonus I decided to create this as an AIR application, which can be downloaded here. If you are interested in Adobe AIR keep an eye out for some AIR tutorials coming soon and check out the Adobe AIR Site. If you have any questions (or comments), please feel free to leave them below.
Posted in Flex, All Tutorials by The Fattest |

August 16th, 2007 at 12:00 pm
That’s great tutorial, and help me a lot. Thank you.
August 21st, 2007 at 9:57 pm
This is a great tutorial, but I’m having a little trouble getting it to work. The code compiles and runs. When I enter a value in the ‘Search Music’ box and click the ‘Search’ button, I get the message “Required parameter ‘ItemSearch’ not found in input arguments.”
I’m not sure what I’ve got wrong. Do you have any advice?
August 22nd, 2007 at 8:59 am
That sounds like something is not quite correct with the operation setup. Can you post the code you have for the WebService? The parameters like to be a pain in the butt sometimes, everything has to be very precise.
August 29th, 2007 at 1:17 pm
Thanks for the cool tutorial. What do you think are the advantages and disadvantages of creating the webservice as you did VS leaving out the “operation” tag and just calling the method directly. In this case, in your actionscript code, couldn’t you have made the call “AmazonSearch.ItemSearch(AWSAccessKeyId, keywords, ‘Music’, 20)”? Thanks.
August 29th, 2007 at 3:12 pm
For the amazon web service it wouldn’t work that easily because the wsdl definition for ItemSearch has actually many arguments and most are optional so it wouldn’t know which argument is which in your example. You can however call the service programmtically. I actually do this in another project. This would look something like the following:
As for defining the operations you get some extra functionality when you define them. The biggest in my opinion is the result format property. Another is having a specific function to process the results of the different operations. Last I feel that it is just a good practice to know which operations are being used for maintainability.
August 30th, 2007 at 10:36 am
Scott S.,
I had the same error. Fixed it by updating my Flex Builder 2.01 installation with all three hotfixes available from the Adobe website.
September 27th, 2007 at 6:41 pm
This is a great tutorial.
they’d made a similar application for amazon web services with flextense.
link www.flextense.net/Video.aspx
October 3rd, 2007 at 5:46 am
Hi, i’ve just noticed that the tutorial is no longer working with the the flex 3 beta 2. I keep on getting the “Error #1009: Cannot access a property or method of a null object reference.” error. Looked throw the response from amazon and the flex but everything seems ok. what did adobe change?
October 3rd, 2007 at 9:13 am
I don’t know what would break it but I will hunt down the issue. Thanks for the heads up.
October 3rd, 2007 at 12:37 pm
Here are two results from the AWS, the first result gives the null reference the second result does not give the null reference. (As a side note I couldn’t get WSDL introspection in Flex Builder 3 Beta 2 to work with the Amazon WSDL(7/16/07), the error message was generic and the .log file had java errors I didn’t understand.)
First result:
TrueB000002TPEhttp://www.amazon.com/gp/redirect.html%3FASIN=B000002TPE%26tag=ws%26lcode=sp1%26cID=2025%26ccmID=165953%26location=/o/ASIN/B000002TPE%253FSubscriptionId=11111111111http://ec1.images-amazon.com/images/I/01ENQSCME6L.jpg
Second result:
TrueB00000DDTRhttp://www.amazon.com/gp/redirect.html%3FASIN=B00000DDTR%26tag=ws%26lcode=sp1%26cID=2025%26ccmID=165953%26location=/o/ASIN/B00000DDTR%253FSubscriptionId=11111111111111Blind Melon
This poorly formed xml snippet also is often (always?) included when the null reference error happens. Is this part of the chunked format?
1198
October 3rd, 2007 at 12:41 pm
Sorry about the formatting, I thought that might happen. I will escape the xml if I can and re-post.
October 3rd, 2007 at 1:07 pm
Here are two results from the AWS, the first result gives the null reference the second result does not give the null reference. (As a side note I couldn’t get WSDL introspection in Flex Builder 3 Beta 2 to work with the Amazon WSDL(7/16/07), the error message was generic and the .log file had java errors I didn’t understand.)
First result:
<Items>ltRequest>ltIsValid>True</IsValid></Request><Item><ASIN>B000002TPE</ASIN>
<DetailPageURL>http://www.amazon.com/gp/redirect.html%3FASIN=B000002TPE%26tag=ws%26lcode=sp1%26cID=2025%26ccmID=165953%26location=/o/ASIN/B000002TPE%253FSubscriptionId=11111111111
</DetailPageURL><SmallImage><URL>http://ec1.images-amazon.com/images/I/01ENQSCME6L.jpglt/URL>
Second result:
<Items><Request><IsValid>True</IsValid></Request>ltItem><ASIN>B00000DDTR</ASIN>
<DetailPageURL>http://www.amazon.com/gp/redirect.html%3FASIN=B00000DDTR%26tag=ws%26lcode=sp1%26cID=2025%26ccmID=165953%26location=/o/ASIN/B00000DDTR%253FSubscriptionId=11111111111111</DetailPageURL>
<ItemAttributes><Artist>Blind Melon</Artist>
This snippet also is often included.
<Amount>1198</4ebAmount>
October 3rd, 2007 at 1:19 pm
drat it all, nevermind.
October 9th, 2007 at 8:10 am
Hi, just wanted to check if you have manage to find why this example is not working with the latest flex 3 beta?
October 9th, 2007 at 9:28 am
I just got it downloaded yesterday I am going to look into this tonight.
October 31st, 2007 at 3:26 am
Thanks a Lot , great work, a wonderful resource, all praise.
December 2nd, 2007 at 8:14 pm
Pasted the completed code and ended up in the same problem as
Scott S. Says:
August 21st, 2007 at 9:57 pm
“The code compiles and runs. When I enter a value in the ‘Search Music’ box and click the ‘Search’ button, I get the message “Required parameter ‘ItemSearch’ not found in input arguments.”
Using Flex Version 2.0.1 build 155542
December 2nd, 2007 at 8:53 pm
Later I dug out of the postings that there were hot fixes. Did the three hotfixes and now have Version 2.0.1 build 166910. Noted no change in build number from hotfix2 to hotfix 3.
When seaching the Alert shows:
Error #1006: getAttributeByQName is not a function.
Live link: http://www.hosfordusa.com/testing/AmazonMusic.html
December 3rd, 2007 at 1:16 pm
Lon,
The hotfixes for Flex should resolve any issues. I compiled and ran the application with Flex 2 hotfix 3 today and it worked fine.
December 11th, 2007 at 6:48 am
I am having the same issue:
Error #1009: Cannot access a property or method of a null object refrence.
I’ve looked around and can only really find this:
http://livedocs.adobe.com/flex/2/langref/runtimeErrors.html
Anyone have an answer?
I’m using flex builder 3 beta 2
December 11th, 2007 at 9:43 am
topcat,
I am working on the solution for this, I have identified the issue is with the parsing of the result in Flex 3. It seems that the resultFormat needs to be changed to e4x for both webservice calls and then the data can be parsed using techniques for e4x. More info on e4x can be found over at adobe’s livedocs.
December 12th, 2007 at 9:32 am
Thanks for the reply, I think this bit of info could be useful: I inserted the tag when I run this I am able to see the soap call works and info is returned. So if there are other people who are stuck trying to work out the reason for your problem put the trace tag in and see what Amazon are returning to you. You should see something like:
Role=”Performer”>James CaviezelMaia MorgensternChristo JivkovFrancesco De
If not then start with the Amazon call. However you will then be on the same problem as us i.e how do you get to the info when its returned
February 15th, 2008 at 5:58 pm
FOr those of YOu running Flex 3. Dont forget to index your calls to currentItemDetails at 0. For this App there should just be ONE returned anyway.
Example:
In the source it says…
currentItemAttr = currentItemDetails.ItemAttributes;
it should say….
currentItemAttr = currentItemDetails[0].ItemAttributes;
Also, make sure you actually have a AWSAccess Key and have put it in the code becuase the existing source only shows a bunch of Xs.
March 14th, 2008 at 4:04 pm
I had to make the following change to get this to work with Flex 3:
From
currentItemDetails = res.result.Items.Item;
to
currentItemDetails = res.result.Items.Item[0];
Thanks for a helpful tutorial!!
April 3rd, 2008 at 7:47 am
Great tutorial
I had the same error as nr2 Scott S. after “Search”, S get “Required parameter ‘ItemSearch’ not found in input arguments”.
Except the AccessKeyId, everything is the same in the source code.
I use Flex3.0
Does anyone know what’s wrong?
April 21st, 2008 at 6:35 pm
When using Flex 3.0 and testing locally I get - TypeError: Error #1010: A term is undefined and has no properties.
April 21st, 2008 at 6:55 pm
Got it working with Flex 3.0. Needed 1) AWSAccessKeyId and 2) what Hyperion76 said. Thanks for the tutorial.
May 17th, 2008 at 1:35 am
Hi,
i have used same code in FLEX3, but i am getting following error
“required parameter ‘MarketplacedDomain’ not found in input argument”
please help me how to fix this error
thnaks
May 20th, 2008 at 12:30 pm
This code now generates ’security error accessing URL’….
What happened?
May 20th, 2008 at 1:02 pm
Not sure I will check this out in the near future.
June 20th, 2008 at 4:13 am
Any solution to the “security error accessing URL” problem ed mentioned?
June 21st, 2008 at 6:19 pm
Looks like it’s related to the new Flash Player 9,0,124,0 with security updates that probably require amazon to update the crossdomain.xml file to include .
Last i checked they hadn’t done that.
All this securety stuff with flash is SO ANOYING!!!
Regards
June 27th, 2008 at 4:09 am
plz urgent help needed…its woking fine but i want to search other properties like Rating,Price of product and vendor …urgent help is required.
Thanks in advance friends!
June 27th, 2008 at 5:39 am
where to get API of amazon for flex3?
how to know which methods to call for a particular task ?
plz help m a newbi
July 1st, 2008 at 11:44 am
I wrote about the answer to the securty issue here at my blog:
http://sdflex.org/blog/air/amazon-web-services-cont
July 16th, 2008 at 1:54 am
can anybody tell me if they have working code of ebay shopping api
or any site where i can get that working code .
For flex 3 only.
thnx!
August 15th, 2008 at 4:27 pm
Hi,
This was very helpful. I did have to add the Amazon destination to my proxy-config.xml. Was still getting Security error, but then I realized I had to match the destination id in it to my WebService (serviceName):
Proxy-config.xml:
http://ecs.amazonaws.com/AWSECommerceService/2008-06-26/AWSECommerceService.wsdl
http://soap.amazon.com/onca/soap?Service=AWSECommerceService
This took care of MY security error.
September 19th, 2008 at 3:22 am
I have also got “Security error accessing url”.