View Full Version : URLLoader... which url?
shadedecho
July 29th, 2008, 07:36 PM
When a URLLoader (that was passed a URLRequest object) fires an event like the 'complete' event, the 'target' attribute of the event parameter is the URLLoader... however, because of certain program logic, I need to be able to tell from the URLLoader what the URLRequest (or more specifically, what the url) is for that particular event response. For instance, if I have 10 different urls that I fire off 10 different loaders with, I need a single event handler to be able to tell which url the response came from is for certain processing.
I have been unable to figure out how to take the URLLoader reference and get it to tell me the URLRequest or the url it comes from. Why?
What could possibly be the reason for the URLLoader not keeping a (public) reference to the URLRequest used by the last load call? This seems like an incredibly dull and limiting decision by the designers of this class.
Anyone have any thoughts?
skial
July 31st, 2008, 06:46 AM
Well one way to do it is to enter all loaded data into a array with a "id" also pushed into the array. Below is a quick class that I made that does this.
package
{
import flash.events.*;
import flash.display.*;
import flash.net.*;
/**
* ...
* @author $(Skial Bainn)
*/
public class example
{
private static var loader:URLLoader;
private static var LoaderList:Array = new Array;
private static var internalHolder:Array = new Array;
private static var url:URLRequest;
private static var prop:Object;
public static function add(setting:Object):void
{
prop = new Object;
prop.onC = setting.onC;
internalHolder.push(setting.id);
LoaderList.push(setting.path);
}
public static function start():void
{
for (var j:int = 0; j < LoaderList.length; j++)
{
trace(LoaderList[j]);
loader = new URLLoader;
loader.addEventListener(ProgressEvent.PROGRESS, onProgress);
loader.addEventListener(Event.COMPLETE, prop.onC);
loader.addEventListener(Event.INIT, onInit);
loader.load(url = new URLRequest(LoaderList[j]));
//
internalHolder.push(loader);
trace(internalHolder.toString());
}
}
private static function onProgress(e:Event):void
{
trace("progress");
}
//
private static function onInit(e:Event):void
{
trace("ready");
}
//
private static function onComplete(e:Event):void
{
trace("complete");
//
loader.removeEventListener(ProgressEvent.PROGRESS, onProgress);
loader.removeEventListener(Event.COMPLETE, onComplete);
loader.removeEventListener(Event.INIT, onInit);
}
public static function getContent(id:String)
{
var _arrayLength:int = internalHolder.length;
var _arrayHalf:Number = _arrayLength / 2;
var _itemPosition:int;
for (var i:int = 0; i < _arrayLength; i++)
{
var _t:String = internalHolder[i];
if (_t.match("" + id))
{
_itemPosition = i;
}
}
//
return internalHolder[Math.ceil(_itemPosition + _arrayHalf)];
}
}
}
The "add" function allows you to enter a "id", "path", and "onC" (onComplete) function, so you know you are able to access the loaded data.
Below just shows a quick example of how to use it.
package
{
import flash.display.*;
import example;
import flash.events.*;
/**
* ...
* @author $(Skial Bainn)
*/
public class example2 extends Sprite
{
public function example2()
{
example.add( { id:"something1", path:"main.xml", onC:onComplete } );
example.start();
}
//
private function onComplete(e:Event):void
{
trace("complete");
trace(example.getContent("something1").data);
}
}
}
This traces the loaded xml file in the output window.
<?xml version="1.0" encoding="utf-8" ?>
<main>
<filePath>xml/something0.xml</filePath>
<filePath>xml/something1.xml</filePath>
<filePath>xml/something2.xml</filePath>
<filePath>xml/something3.xml</filePath>
<filePath>xml/something4.xml</filePath>
</main>
This is a very basic loader for more than one item. To be able to get the content once it has completed, you need to use "getContent" and pass the "id" of the item.
Again this is a very basic, badly coded example.
Cheers:beer2:
Skial
shadedecho
July 31st, 2008, 09:14 AM
What I figured was that there were two ways to slice this:
1. Have a single function that acted as the onComplete handler for all instances of URLLoader... and in this case, a call to that handler would have a reference to the URLLoader instance, but to figure out what URL was used for it, I'd have to traverse through a list (an array for instance) of stored URLLoader instances, which each item specifically relates (like with it's index or some other external property) a URLLoader to a url.
This is obviously painfully inefficient because each onComplete event requires an N-traversal of the list to find it's reference. So O(N^2). :(
2. Create a function reference for *each* instance of URLLoader... either by a dynamic anonymous function which knows the URL by using a closure (what I ended up doing) or by dynamically creating an object wrapper which gives object context to a surrounding object method function, so each object instance is bound to a URLLoader instance (what you have shown).
All 3 concepts work. But, the two approaches to #2 (yours and mine) both sorta violate what I wanted for simplicity and memory efficiency, which is a *single* handler function that is able to handle all complete events, and from the URLLoader object itself, backtrack reference in some way to get the url it used.
If URLLoader was "dynamic", I could just assign a dynamic "url" property to it, which would be available to the handler... but alas, it is not.
So, it appears that the only option is to have a dynamic object/function for each URLLoader instance to provide the right context to save the url external to the URLLoader object itself.
Still very frustrating to me to have to jump through those hoops. Isn't a pretty common thing to have multiple URLLoader instances, and want them to use a common handler?
shadedecho
July 31st, 2008, 09:25 AM
Just for posterity sake, my solution (the dynamic anonymous function with closure) looks something like this:
var url:String = "http://www.example.com/";
var loader:URLLoader = new URLLoader();
loader.addEventListener(Event.COMPLETE, function(cEvent:Event) {
trace("Complete! Url:"+url);
trace("Data:"+cEvent.target.data);
});
loader.load(new URLRequest(url));
The key part here is that the dynamic function will retain a "closure" reference to the variable url even though the function will be called in a different scope at a later time. Reason #1053843 why I love closures! :)
NOTE: the "anonymous function" approach works unless you will need to do a removeEventListener() call later, like if you are trying to delete the URLLoader completely. Because that removeEventListener() needs an exact reference to which function to unbind, and you no longer have a reference to the function because it was anonymous and passed only as a dynamic parameter to addEventListener, you will be unable to unbind it.
So, in my case, I actually did something a little more complicated than the above. I actually created a dynamic anonymous function, but saved it in an array first. Then I passed it to addEventListener. But, later, when I need to unbind it, I still have reference to it in the array, so I can call removeEventListener() as needed.
skial
July 31st, 2008, 09:59 AM
Cool idea, nice that you figured out a better way to do it.
Cheers
Skial
flexytime
December 11th, 2009, 08:33 AM
Hi,
I've also hit this same problem and was wondering what your thoughts were on Extending the URLLoader to create a new URLRequestLoader Class which automatically stores a reference to the urlRequest object that is passed in via the Constructor.
You'd use it something like this...
private var urlReqLoader: URLRequestLoader(urlRequest);
urlReqLoader.addEventListener(complete, loaded);
private function loaded(event:Event)
{
var urlLoader:URLRequestLoader= event.currentTarget;
var urlReq:URLRequest = urlLoader.urlRequest as URLRequest;
var URLstring = urlReq.data;
//et voila!
}
// Can you use this code - yes of course but please keep my details in place... Thanks. Any and all comments are very welcome...
// (c) 2009 Nick Middleweek - flexytime
// http://blog.middleweek.co.uk
package comps
{
import flash.net.URLLoader;
import flash.net.URLRequest;
public class URLRequestLoader extends URLLoader
{
public var urlRequest:URLRequest;
public function URLRequestLoader(request:URLRequest=null)
{
//TODO: implement function
this.urlRequest = request;
super(request);
}
}
}
Freaken
December 11th, 2009, 08:53 AM
I don't really know how you could reference the URLRequest, but a solution that I see would be to use a Dictionary object that uses the URLLoader as the key and add the url of the urlrequest as the value.
dict[URLLoader] = "http://......."
that way, you can check in the dictionary in the completeEventHandler function (or whatever you call it) by using evt.target as the key. That should do the trick.
Powered by vBulletin® Version 4.1.10 Copyright © 2012 vBulletin Solutions, Inc. All rights reserved.