UPDATE 2: The issue has been resolved in a new nightly build of the SDK. It should be fixed when 3.6 is release.
I ran into an issue using the HTTPService object that involved removing event listeners that were added inside MXML.
Here is a snippet of code in the project. This will open an xml file and load some data into a List.
//this will fetch all our content. private var _httpService:HTTPService = new HTTPService; private function init() : void { _httpService.url = "stub/data/albums.xml"; _httpService.resultFormat = "e4x"; //set up http call _httpService.addEventListener(ResultEvent.RESULT, populateList); _httpService.send(); } private function populateList(event:ResultEvent) : void { //set the data provider albumsComboBox.dataProvider = event.result.album; //remove listener _httpService.removeEventListener(ResultEvent.RESULT, populateList); }
This will open another xml file and populate the TileList with images.
private function loadAlbum(event:ListEvent) : void { _httpService.url = "stub/data/" + event.currentTarget.selectedItem.@images; _httpService.resultFormat = "e4x"; //set up http call _httpService.addEventListener(ResultEvent.RESULT, populateTilelist); _httpService.send(); } private function populateTilelist(event:ResultEvent) : void { //set the data provider imageGrid.dataProvider = event.result.photo; //remove listener _httpService.removeEventListener(ResultEvent.RESULT, populateTilelist); }
When loadAlbum calls send() it will call populateList first and then goto populateTilelist. removeEventListener didn't work. I posted a question to StackOverflow with the problem and got some great answers.
To get around this problem I created a HTTPService wrapper that can be reused with no issues because it's an ActionScript Class. It's very light weight, doesn't include everything under the sun, but it gets the job done in a pinch. This is the 3rd revision of the class. It's been cleaned up and there are fewer methods now.
package util { import mx.controls.Alert; import mx.rpc.AsyncResponder; import mx.rpc.AsyncToken; import mx.rpc.IResponder; import mx.rpc.events.FaultEvent; import mx.rpc.events.ResultEvent; import mx.rpc.http.HTTPService; public class HTTPServiceWrapper { //reference for connections private var _httpService:HTTPService; private var _alertTitle:String = "HTTPService: An error occured"; // Holds all the tokens and callbacks private var _processingQueue : Object = {}; /** * Create the HTTPService * */ public function HTTPServiceWrapper() : void { //create the service _httpService = new HTTPService; } /** * Get content from URL * @param url * @param resultFormat * @param callBack * @param request * @param returnErrorEvent * */ public function getContent(url:String, resultFormat:String, callBack:Function, request:Object = null, returnErrorEvent:Boolean = false) : void { var asyncToken:AsyncToken; var internalIResponder:IResponder; //set the url and result format _httpService.url = url; _httpService.resultFormat = resultFormat; _httpService.request = null; //check for key/value pairs to be sent in the url if(request) { _httpService.request = request; } //send the request asyncToken = _httpService.send(); internalIResponder = new AsyncResponder(onGetContentHandler,onFault, asyncToken); asyncToken.addResponder(internalIResponder); //give token unique ID asyncToken = tokenID(asyncToken); _processingQueue[asyncToken.ID] = new QueueObject(callBack,returnErrorEvent); } /** * This is the handler event for getContent. The callBack should accept a ResultEvent * * @param event * @param token * */ private function onGetContentHandler(event:ResultEvent, token:AsyncToken) : void { var queueObject:QueueObject = _processingQueue[token.ID]; queueObject.callBack(event); } /** * This is the fault event for the class. The callBack should accept a null ArrayCollection and a FaultEvent if * you want to handle the error. * * @param event * @param token * */ private function onFault(event:FaultEvent, token:AsyncToken) : void { var queueObject:QueueObject = _processingQueue[token.ID]; //handle the fault if(queueObject.returnErrorEvent){ queueObject.callBack(null, event); } else { var displayMessage:String = event.fault.faultString; displayError(displayMessage); queueObject.callBack(null); } } /** * This will add a unique identifier to a token * @param token * @return * */ private function tokenID(token : AsyncToken) : AsyncToken { token.ID = Math.random(); return token; } /** * Displays the error on a fault event in the content service * @param event * */ private function displayError(displayMessage : String) : void { Alert.show(displayMessage, _alertTitle, Alert.OK); } } } class QueueObject{ public var callBack:Function; public var returnErrorEvent:Boolean; public function QueueObject(callBack:Function, returnErrorEvent:Boolean){ this.callBack = callBack; this.returnErrorEvent = returnErrorEvent; } }
Using the class is really simple. Use getContent and pass it the file you want to load, the format it should be loaded in and the call back you want it to call after the file is retrieved. There are two extra properties I'm not using right now, but they will be helpful in the future. The fourth parameter accepts an Object of key/value pairs to be sent with the URL.The fifth parameter lets you capture the result of an error if one occurs. The call back should accept a FaultEvent object if you want to capture the error. If you let HTTPServiceWrapper handle the error an Alert window will pop up showing the error.
//http wrapper private var _httpServiceWrapper:HTTPServiceWrapper = new HTTPServiceWrapper; /** * Runs when the application loads * */ private function init() : void { //get the list of albums _httpServiceWrapper.getContent("stub/data/albums.xml", "e4x", populateList); //set event listeners for app albumsComboBox.addEventListener(ListEvent.CHANGE, loadAlbum); } /** * This will populate the album list * */ private function populateList(event:ResultEvent) : void { //if the result is null don't set it if(!event) { return; } //set the data provider albumsComboBox.dataProvider = event.result.album; } /** * This will fetch the selected album * */ private function loadAlbum(event:ListEvent) : void { _httpServiceWrapper.getContent("stub/data/" + event.currentTarget.selectedItem.@images, "e4x", populateTilelist); }
This cut quite a bit of bloat out of my app and is also very reusable which saves time in the long run. The final version of this code is hosted here. You can view source on it by right-clicking in the app and selecting "View Source". Please let me know if you have any questions about it.