Tuesday, October 14, 2008
YahooMap Api for Flex 3 is Buggy
Some of the bugs related to the marker manager are:
1. _yahooMap.markerManager.markers.splice(1,1);
does nothing. Even though the markers is an array and should allow the Splice operation to go through but nothing happens
2. _yahooMap.markerManager.removeAllMarkers()
gives an annoying error.""Error# 1009: Cannot access a property or method of a null object reference"
3. _yahooMap.markerManager.removeMarker(obj as Marker) is also buggy.
The intended behavior its that it should just remove the particular marker that is passed in the call, instead it removes all the markers that are present after the specified marker. So assuming if the markers array is [1,2,3,4] where the numbers are the marker ids. The call _yahooMap.markerManager.removeMarker(2) will give [1] instead of [1,3,4]
4. _yahooMap.markerManager.pop(): doesnot remove the element from the markers array it just gives the reference to it. So even after the pop() operation the marker manager will contain the recent most marker.
Solution for removing the markers from the marker manager
I have used the a work around for removing the items from the marker manager. I exploited the current behavior(buggy one see point 3) of _yahooMap.markerManager.removeMarker(obj as Marker).
The function is removing all the elements that are present after the marker passed in the call. So I am passing the recent most marker so that it removes latest marker and the markers after that (which will be null). Let me give one example
say the marker manager contains markers #: 1,2,3,4
we will use the following code to remove the markers. so after every pass we get
pass 1: 1,2,3 (4 is removed and there was nothing after that so only 4)
pass 2: 1,2
pass 3: 1
pass 4: null
private function removeYahooMarkers():void
{
var len:int=_yahooMap.markerManager.markers.length;
for(var i:int=0;i<len;i++)
{
var obj:Object= _yahooMap.markerManager.markers.pop();
if(obj!=null)
_yahooMap.markerManager.removeMarker(obj as Marker)
if(obj==null)
return;
}
}
Even the overlayManager is buggy.
There is some refresh problem. In case of removing any overlay, the overlay manager does not refreshes the overlays array.
So if you do
_yahooMap.overlayManager.removeAllOverlays(); it would have removed the overlays internally but the overlays array still contains the references (something like dangling pointers in C/C++)
one workaround is to just call the removeAllOverlays(0 again so
_yahooMap.overlayManager.removeAllOverlays();
_yahooMap.overlayManager.removeAllOverlays();
Tuesday, May 13, 2008
create a validator PwdValidator.as
package valueObjects
{
import mx.validators.ValidationResult;
import mx.validators.Validator;
public class PwdValidator extends Validator {
// Define Array for the return value of doValidation().
private var results:Array;
// this will help in getting the status
public static var VALID:Boolean = true;
// Constructor.
public function PwdValidator() {
// Call base class constructor.
super();
}
// Define the doValidation() method.
override protected function doValidation(value:Object):Array {
//var pwd: Password = value as Password;
var p1: String = value.first;
var p2: String = value.second;
results = [];
results = super.doValidation(value);
if(p1 == p2)
{
VALID=true;
return results;
}
else
{
results.push(new ValidationResult(true, null, "Mismatch",
"Password Dosen't match Retype!"));
VALID=false;
return results;
}
}
}
}
Now the flex code should be passing the value of the password and confirm password textbox. This is done as
<mx:Model id ="pass">
<name>
<passwords>
<first>{txtPassword.text}</first>
<second>{txtConfirmPassword.text}</second>
</passwords>
</name>
</mx:Model>
// this is the way to pass the multiple controls input to the validator.
<val:PwdValidator id="custPass" source="{pass}" property="passwords" listener="{txtPassword}"/>
<mx:Form labelWidth="110">
<mx:FormItem required="true" label="Password">
<mx:TextInput id="txtPassword" displayAsPassword="true" />
</mx:FormItem>
<mx:FormItem required="true" label="Confirm Password">
<mx:TextInput id="txtConfirmPassword" displayAsPassword="true" change="custPass.validate()"/>
</mx:FormItem>
</mx:Form>
Saturday, April 19, 2008
There are times when the data that you obtain is in the form of XML or a string that contains XML tags. For displaying the data in datagrid, list, trees we desire to convert the data in the XMLListCollection. We need to perform the following steps to get the XMLListCollection.
1. Lets assume that we want to convert the following string to the XMLListCollection
var xmlStr:String="<root>
<person>
<name>Rohit</name>
<surname>Agarwal</surname>
<phone>5551234</phone>
<age>24</age>
</person>
<person>
<name>Richa</name>
<surname>Mittal</surname>
<phone>5552341</phone>
<age>23</age>
</person>
<person>
<name>Puneet</name>
<surname>Jain</surname>
<phone>5553412</phone>
<age>23</age>
</person>
</root>";
2. Convert the string to XML
private var pat:XML ;
pat=new XML(xmlstr);
3. Convert the XML to XMLList. We want to have individual persons as seperate XML objects
private var patList:XMLList;
patList = pat.person;
4. Convert the XMLList to XMLListCollection
[Bindable]
private var patListCol:XMLListCollection;
patListCol = new XMLListCollection(patList);
In this way the XMLListCollection is created from the XML
I wanted to use AdvancedDataGrid control of Flex 3, for which the best way to represent data is in the form of an ArrayCollection. The problem was that the data available to me was in the form of an string(XML serialized as string) and not objects. So I wanted to convert the XML to ArrayCollection.
Steps
1. The following is the string that I wanted to convert:
var xmlStr:String="<root>
<person>
<name>Rohit</name>
<surname>Agarwal</surname>
<phone>5551234</phone>
<age>24</age>
</person>
<person>
<name>Richa</name>
<surname>Mittal</surname>
<phone>5552341</phone>
<age>23</age>
</person>
<person>
<name>Puneet</name>
<surname>Jain</surname>
<phone>5553412</phone>
<age>23</age>
</person>
</root>";
2. Convert the string to XMLDocument
var xmlDoc:XMLDocument = new XMLDocument(xmlStr);
3. Convert the XMLDocument to object by using SimpleXMLDecoder
var decoder:SimpleXMLDecoder = new SimpleXMLDecoder(true);
var resultObj:Object = decoder.decodeXML(xmlDoc);
4. Declare an ArrayCollection
[Bindable]
private var patArrayListCollection:ArrayCollection = new ArrayCollection();
5. Add the resultobj to the ArrayCollection
patArrayListCollection.addItem(resultObj.root.person);
The important thing to note here is the composition of the patArrayListCollection is in the form
[0] --main object
[0]-sub object 1
[1]-subobject 2
[2]-subobject 3
This is not in a form that we require to make it a data provider for the AdvancedDataGrid. The Grid requires the ArrayCollection to have
objects in the form
[0]-object 1
[1]-object 2
[2]-object 3
So to get this form the following step is done
6. var tempArray:ArrayCollection=patArrayListCollection.getItemAt(0)as ArrayCollection;
7. Finally assign tempArray to the AdvancedDataGrid
myADG.dataProvider=tempArray;
Sunday, April 13, 2008
SecurityError: Error #2148: SWF file file:///C:/Documents and Settings/UserProfile/Desktop/flexstore/bin-release/flexstore.swf cannot access local resource myFile.swf. Only local-with-filesystem and trusted local SWF files may access local resources.
This runtime error is thrown in Flex 3 if some where in the application you are trying to do a httpservice and use a resource from the local folder.
<mx:HTTPService id="fillTreeByCatRPC" url="categories.xml" resultFormat="e4x" />
I have seen this error only in Flex 3, Flex 2 never gave this error.
I did some google and found some solutions. Almost all the solutions suggests that one should change the flex compiler settings.
"add these arguments to the compiler (via Properties - Flex Compiler) : -use-network=false " http://curtismorley.com/2007/08/31/flash-cs3-flex-2-as3-error-2148/#comment-3714
The problem with this solution is that once the settings are changed then the application will not be able to read any remote resource. so the below request will fail
<mx:HTTPService id="fillTreeByCatRPC" url="http://www.resources.com/categories.xml" resultFormat="e4x" />
After doing further searches on Internet, I found that it is the setting problems of the flash player that cause the problem. Basically one needs to set the security settings of the flash player so that it allows the swf file to access the resources on the local system. This is done by visiting the following page
http://www.macromedia.com/support/documentation/en/flashplayer/help/settings_manager04.html
Make sure that the Global security settings are set to Always Allow
The following screenshot is what we are looking for
In all probability the error is gone.
Sunday, March 9, 2008
Customized event in Flex 2
Events are a way of sharing data between two components. Events also acts as a asynchronous message suggesting that some input has arrived to the receiver of the event. For e.g. when a person clicks on the button control the event click is generated signifying that a user input in the form of a click has occurred. Once the event has occurred we need to take some action like on button click display the “hello world” in a text box. The action that we take is known as handling of the event. Or in other words we need to specify the even handler. Event handler is a function that specifies what needs to be done on that particular event.
The following code snippet makes it clear
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx=="http://www.adobe.com/2006/mxml"
layout="vertical">
<mx:Script>
<![CDATA[
//Event handler for the button click function
private function buttonEventHandler(evt:Event):void{
taTest.text="Hello World";
}
]]>
</mx:Script>
<mx:Button id="btTest" label="Click Me" click="buttonEventHandler(event)" />
<mx:TextArea id="taTest" />
</mx:Application>
buttonEventHandler is the event handler that will be called when the button is clicked. One salient point to note is the argument passed in the handler. We pass the object of type Event i.e event. The object “event” is always available in the application and we need to use it at the time of calling an event handler.
Sometimes it happens that we need to transfer some data between the two components.
For e.g. there may be a component which is responsible to get the data from the server using a httprequestserver. Component populates the data in a collection and now wants to send to another component which implements the list or data grid to display the information to the end user. The events are the way through which this can be done. The catch here is that the event thrown by one component should be available in the other component. So the component1 when it is done with populating the collection will throw an event indicating which needs to be handled in the component2 responsible to display the collection in the datagrid. It happens because of the way the events are transmitted from the component to the Application. Before getting into the transmission concept we need to understand one more concept known as event listener. Event Listener signifies that the component wants to receive a particular event and so it wants to register itself as one of the recipients.
We use the function addEventListener(event name , event handler) to do that. Now you must be wondering that the above button code did not had any such function call then how were we able to handle the event. The simple answer is that the event that we talked about was provided by the flex framework and so every component has got an implicit event listener for such events.
But the events that we are now going to see are the one that user has created himself (customized events). The customized events are used to transfer the data. These are created by the users and so the components will not be aware of them. Event Listener explicitly registers the component to listen to these customized events.
HOW TO WRITE A CUSTOMIZED EVENT
There are 3 stages in order t use a customized event
1. Create customized event
2. Dispatch the customized event
3. Listen the customized event.
Create Stage
Let us see how we actually write a customized event. We are just discussing the salient points here which are required to write a customized event. Syntactical and theoretical details are well covered in the Adobe documents.
Customized event can be declared by writing a class that inherits from Event.
package controls
{
import flash.events.Event;
public class ItemAddedEvent extends Event
{
var itemDescription:String; //an item to be added to the cart
public static const ITEMADDEDEVENT:String ="ItemAddedEvent";
public function ItemAddedEvent(description:String)
{
super(ITEMADDEDEVENT,true,true);
itemDescription=description;
}
override public function clone():Event{
return new ItemAddedEvent(itemDescription);
}
}
}
These are the steps that need to follow for defining any customized event
1. The class has to extend Event
2. Declare what ever data type you want to transfer by this event.
var itemDescription:String; //an item to be added to the cart
3. One can give a name to the event for easy reference. In our case it is ITEMADDEDEVENT. We can give multiple names to the same event. This is required when we want to use the same event in different places. For e.g. in a shopping cart application we need to perform add item to cart, delete item from the cart and update the quantity of the item in the cart. In all the operations the same item type data is required to be passed. So we can define a single customized class that favors for the transfer of a item type data. But the same event class can be used for all the 3 functions. We will give 3 names so that we remember for what all purpose we are calling this class.
public static const ADD:String = "addtocart";
public static const REMOVE:String = "removefromcart";
public static const UPDATE:String = "updatecart";
4. In the Constructor we need to call super(). The parameters required to be passed are the type of the event (ADD,REMOVE,UPDATE) and the optional parameters specifying the properties of the event.
Super(type, isbubble,iscancellable)
The second argument signifies whether the event is allowed to bubble. i.e. an event thrown by a component is allowed to be listened by the components that are up in the hierarchy.
5. clone() . This is required as per the Adobe document. We need to override the clone function and in the body just instantiate the customized event and return the object so created.
override public function clone():Event
{
return new ItemAddedEvent(itemDescription);
}
That is all to be done for a creation of an event.
Dispatch stage:
The event is thrown by the component in this stage. There is a well defined procedure to be followed to do that.
Lets take an example and do that
<?xml version="1.0" encoding="utf-8"?>
<mx:VBox xmlns:mx="http://www.adobe.com/2006/mxml">
<mx:Metadata>
[Event(name="addItemEvent", type="controls.ItemAddedEvent")]
</mx:Metadata>
<mx:Label text="Item name:"/>
<mx:TextInput id="enteredItem"/>
<mx:Button width="104" height="28" cornerRadius="10" fillColors="[#00ff00, #00B000]" label="Add Item" fontSize="12" click="greenClickEventHandler()"/>
<mx:Script>
<![CDATA[
import controls.ItemAddedEvent;
private function greenClickEventHandler():void{
trace("Ouch! I got clicked! Let me tell this to the world.");
// throws the event and the passes the string to the customized event object
dispatchEvent(new ItemAddedEvent(enteredItem.text)); // passing the string
}
]]>
</mx:Script>
In the example
<mx:Metadata>
[Event(name="addItemEvent", type="controls.ItemAddedEvent")]
</mx:Metadata>
The metadata section is an optional section that tells that these are the events this component is going to dispatch. It is more for giving the information to the developer that this particular code dispatches some event. This is useful since the other components by looking at the metadata section can find out the events they can listen to.
We give the name of the event (addItemEvent) and the location of the class (controls.ItemAddedEvent). Note we could have also given ITEMADDEDEVENT in place of addItemEvent.
private function greenClickEventHandler():void{
trace("Ouch! I got clicked! Let me tell this to the world.");
// throws the event and the passes the string to the customized event object
dispatchEvent(new ItemAddedEvent(enteredItem.text)); // passing the string
}
When the greenClickEventHandler() is getting called on the click event the component is calling the dispatchEvent function. By calling this function the component is actually throwing the customized event. The dispatchEvent function requires the object of the Customized event class as an argument. So essentially we have passed the data structure (in this case string) to the event. This data will be available to all the component who will listen to this event.
Listening
This is the process of registering for the event. The component has to explicitly tell the application that it wants to get the event thrown by the other component and handle the data that is also being passed along with the event. This is done with the help of addEventListener function. The next section will make this concept clearer. For now we can assume that the function allows the component to receive the event and then handle it.
For e.g.
Component called
addEventListener(ItemAddedEvent.ITEMADDEDEVENT,addItemtoCartEventHandler);
to register the event and tell which handler function to call
private function addItemtoCartEventHandler(event:ItemAddedEvent):void{
sample.text+="yes a book has been added"+ event.itemDescription;
}
In the event handler pass an object (event:ItemAddedEvent) of the customized event class type and then use the data passed through the event using the object passed in the argument.(event.itemDescription)
Now the final step is to instantiate the component that will generate the event which is
<controls:newlargebutton />
The way it will work is
Instantiate ----:comp1 will throw the customized event on click----: comp2 will listen to it and call its event handler
Now lets get into the intricacies of how the events are propagated between the components.
In Flex in order to use any component, that component must be instantiated in the application(the application tag).
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="vertical">
comp 1
comp 2
…
comp n
</mx:Application>
The Application is the root of all the components. So if make a hierarchy tree it will look like
<Application>
| |
<Comp1> <comp2>
So we can say that Application is the parent node of comp1 and comp2
Again there can be a situation where we instantiate a component inside another component. Like
<hbox>
<list>
<button>
<hbox>
so list and button are getting instantiated in hbox
so in these cases the hierarchy tree will look like
<Application>
|
<comp1>
|
<comp2>
But in this case also <application> is related to comp2 in a ancestral relationship.
We need to understand this point because of the nature of event propagation. When any component generates an event it can be listened by all the components that are higher in the hierarchy. So an event generated by comp2 can be listened by comp1 and the Application. This means comp1 can have an event listener to register for the event generated by comp2, similar is the case with Application.
So if comp2 has to send any data to comp1, it will simply generate an event which will send the data to the comp1 which would be listening for this event.
There may be a situation where the components are not in the direct hierarchy but are in a sibling relation
<Application>
| |
<comp1> <comp2>
Now an event produced by comp2 will be easily listened by Application, but not by the comp1. But comp2 wanted to make comp1 listen to the event. Now events that are listened by the parent can be listened by the children also through following command
parent.addEventListener(event type,event handler)
This means that comp1 can listen to the events that reaches upto the parent. So the event sent by comp2 reaches Appication. Now this event can be heard by the comp1.
So in order to pass the events from one component to another one has to look at the way they are related to each other and depending on that call the addEventListener().
Lets look at some of the examples for understanding the concepts
E.g.1 There are two components related to each other in a parent child relationship. Comp1: It’s a VBOX containing a button and a textbox.
Comp2: A VBOX containing TextArea
Now the requirement is that in comp1 what ever is entered in textbox should be displayed in Comp2 textarea on the button click. So basically we want to send text string from comp1 to comp2. This is a classical situation where we will write a customized event to transfer the string between the two components.
The way we have arranged the components in the implementation is
<Application>
|
<comp2>
|
<comp1>
so addeventlistener will be called without parent prefix in comp1
Custom Event class (ItemAddedEvent.as)
package controls
{
import flash.events.Event;
public class ItemAddedEvent extends Event
{
var itemDescription:String; //an item to be added to the cart
public static const ITEMADDEDEVENT:String ="ItemAddedEvent";
public function ItemAddedEvent(description:String)
{
super(ITEMADDEDEVENT,true,true);
itemDescription=description;
}
override public function clone():Event{
return new ItemAddedEvent(itemDescription);
}
}
}
Comp1 (newlargebutton.mxml)
<?xml version="1.0" encoding="utf-8"?>
<mx:VBox xmlns:mx="http://www.adobe.com/2006/mxml">
<mx:Metadata>
[Event(name="addItemEvent", type="controls.ItemAddedEvent")]
</mx:Metadata>
<mx:Label text="Item name:"/>
<mx:TextInput id="enteredItem"/>
<mx:Button width="104" height="28" cornerRadius="10" fillColors="[#00ff00, #00B000]" label="Add Item" fontSize="12" click="greenClickEventHandler()"/>
<mx:Script>
<![CDATA[
import controls.ItemAddedEvent;
private function greenClickEventHandler():void{
trace("Ouch! I got clicked! Let me tell this to the world.");
// throws the event and the passes the string to the customized event object
dispatchEvent(new ItemAddedEvent(enteredItem.text)); // passing the string
}
]]>
</mx:Script>
</mx:VBox>
Comp2: (BlindShoppingCart.mxml)
<?xml version="1.0" encoding="utf-8"?>
<mx:VBox xmlns:mx="http://www.adobe.com/2006/mxml"
xmlns:controls="controls.*"
creationComplete="init()">
<mx:Script>
<![CDATA[
import flash.events.Event;
import controls.ItemAddedEvent;
private function init():void
{
// eventlistener
addEventListener(ItemAddedEvent.ITEMADDEDEVENT,addItemtoCartEventHandler);
}
private function addItemtoCartEventHandler(event:ItemAddedEvent):void{
sample.text+="yes a book has been added"+ event.itemDescription;
}
]]>
</mx:Script>
<!—instantiated the comp1 -- >
<controls:newlargebutton addItemEvent="addItemtoCartEventHandler(event)" />
<mx:TextArea id="sample" />
</mx:VBox>
Application (greenbutton.mxml)
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"
layout="vertical"
xmlns:ctrl="controls.*" >
<ctrl:BlindShoppingCart width="350" height="200" />
</mx:Application>
Same example but instead of child parent relation change it to sibling
So the instantiation of the comp1 will not happen inside comp2 instead it will happen in the Apllication
<Application>
| |
<comp1> <comp2>
Custom Event class (ItemAddedEvent.as)
package controls
{
import flash.events.Event;
public class ItemAddedEvent extends Event
{
var itemDescription:String; //an item to be added to the cart
public static const ITEMADDEDEVENT:String ="ItemAddedEvent";
public function ItemAddedEvent(description:String)
{
super(ITEMADDEDEVENT,true,true);
itemDescription=description;
}
override public function clone():Event{
return new ItemAddedEvent(itemDescription);
}
}
}
Comp1 (newlargebutton.mxml)
<?xml version="1.0" encoding="utf-8"?>
<mx:VBox xmlns:mx="http://www.adobe.com/2006/mxml">
<mx:Metadata>
[Event(name="addItemEvent", type="controls.ItemAddedEvent")]
</mx:Metadata>
<mx:Label text="Item name:"/>
<mx:TextInput id="enteredItem"/>
<mx:Button width="104" height="28" cornerRadius="10" fillColors="[#00ff00, #00B000]" label="Add Item" fontSize="12"
click="greenClickEventHandler()"/>
<mx:Script>
<![CDATA[
import controls.ItemAddedEvent;
private function greenClickEventHandler():void{
trace("Ouch! I got clicked! Let me tell this to the world.");
// throws the event and the passes the string to the customized event object
dispatchEvent(new ItemAddedEvent(enteredItem.text)); // passing the string
}
]]>
</mx:Script>
</mx:VBox>
Comp2: (BlindShoppingCart.mxml)
<?xml version="1.0" encoding="utf-8"?>
<mx:VBox xmlns:mx="http://www.adobe.com/2006/mxml" xmlns:controls="controls.*"
creationComplete="init()">
<mx:Script>
<![CDATA[
import flash.events.Event;
import controls.ItemAddedEvent;
private function init():void
{
// eventlistener
//addEventListener(ItemAddedEvent.ITEMADDEDEVENT,addItemtoCartEventHandler); --wont work
parent.addEventListener(ItemAddedEvent.ITEMADDEDEVENT,addItemtoCartEventHandler);
}
private function addItemtoCartEventHandler(event:ItemAddedEvent):void{
sample.text+="yes a book has been added"+ event.itemDescription;
}
]]>
</mx:Script>
<mx:TextArea id="sample" />
</mx:VBox>
Application (greenbutton.mxml)
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"
layout="vertical" xmlns:ctrl="controls.*" >
<ctrl:newlargebutton width="350" height="82" />
<ctrl:BlindShoppingCart width="350" />
</mx:Application>
In case the parent.addeventListener is not given , the event is not dispatched to the comp2 and so on button click the text in the text are is not written.
Since in the example we have given parent.addeventListener, so comp1 and interact to comp2 via parent and we get
Saturday, March 1, 2008
We all know that Flex 2 provides ArrayCollection , XMLListCollection to read XML and consume the data.
However in case the XML contains only a single set of Data entries ArrayCollection are not the right solution.
Assuming the XML is
<dataroot>
<Capital>
<city>Delhi</city>
<country>India</country>
</Capital>
</dataroot>
In order to read this XML you have two ways to do
<mx:HTTPService id="rpcPatHTTP" url="sample.xml" resultFormat="object" result="handlerCapital(event)" />
<mx:Script>
<![CDATA[
[Bindable]
var myAC:ArrayCollection = new ArrayCollection();
private function handlerCapital(event:ResultEvent):void
{
myAC = event.result.dataroot.Capital as ArrayCollection
}
]]>
</mx:Script>
In the above case if you try to get the length of the myAC ArrayCollection, you will get 0
so
myAC.length will give 0
In such cases you should use the XMLListCollection. So the above example will be rewritten as
<mx:HTTPService id="rpcPatHTTP" url="sample.xml" resultFormat="e4X" result="handlerCapital(event)" />
<mx:Script>
<![CDATA[
[Bindable]
var myXML:XMLListCollection;
private function handlerCapital(event:ResultEvent):void
{
myXML = new XMLListCollection(event.result.Capital);
}
]]>
</mx:Script>
Now if you try to find the length of the collection you will get 1 so myXML.length will give 1, which is correct.
So my recommendation is use XMLListCollection over ArrayCollections to store the incoming remote data since you never know the number of entries in the XML file
Saturday, February 16, 2008
I had a tough time in finding the resources that give working examples on this topic. Let me set the context of the problem then this discussion will make more sense.
There are times when you want to display images from various sources on the internet on your flex page, or you have a image server and you want to display the images from the server on to your page.
There are two ways of doing this
1. The server has sent you the image in the form of a byte array and you just want to render that byte array on your flex page
2. To mention the URL of the image and ask flex to display the image from that URL
Note: Only jpg,png,gif and swf files can be displayed.
Method 1 (byte array)
This is quite simple. (courtesy http://www.kineticz.net/blahg/2007/07/26/rending-a-bitmap-from-a-bytearray-in-flash/ )
I am giving the pieces of the code. Basically Assuming that you have your byte array ready with you from the server. I will explain how to render
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" creationComplete="init()">
<mx:Script>
<![CDATA[
private function renderImage():void
{
var byteLoader:Loader = new Loader();
byteLoader.loadBytes(Your_Byte_Array_Name);
imageCanvas.rawChildren.addChild(byteLoader);
}
</mx:Script>
<mx:Canvas id="imageCanvas" left="10" top="189" right="528" bottom="10"/>
</mx:Application>
We essentially have to do the following three steps:
step1 : declare an instance of Loader class.
var byteLoader:Loader = new Loader();
step2: Now load the bytes of the byte array in the loader. This is going to load the bytes in the security context of your application
byteLoader.loadBytes(your_byte_array);
step3: You need to have a canvas control where the image will be displayed so you must have something like
<mx:Canvas id="imageCanvas" left="10" top="189" right="528" bottom="10"/>
step4: Finally load the Byte Array in the canvas
imageCanvas.rawChildren.addChild(byteLoader);
Method 2: Directly accessing the images using Image URL
In this method I will give a sample code that will access the image using the image URL. First I will give the sample implementation and then explain whats need to be done. The code has been taken from http://onflex.org/flexapps/applications/ProgressiveImageLoading/srcview/index.html
<?xml version="1.0" encoding="utf-8"?>
<mx:Application
xmlns:mx="http://www.adobe.com/2006/mxml"
initialize="appInit()"
layout="absolute"
viewSourceURL="srcview/index.html"
backgroundGradientAlphas="[1.0, 1.0]"
backgroundGradientColors="[#FFFFFF, #FFFFFF]">
<mx:Script>
<![CDATA[
// Progressive Image Loading
import flash.net.URLStream;
import flash.net.URLRequest;
import flash.utils.getTimer;
// loader to hold the loaded bytes
public var loader:Loader;
// URLStream to load the image bytes
public var imageStream:URLStream;
// ByteArray to hold aggregate image data
public var imageData:ByteArray;
// Set up initial conditions when the application initializes
public function appInit():void
{
//create a URLStream to load data in. The incoming data will be obtained using URLStream.
imageStream = new URLStream();
//add some event listeners for PROGRESS and COMPLETE. This will help in order to determine when the image download is complete
imageStream.addEventListener( ProgressEvent.PROGRESS , imageStreamProgress );
imageStream.addEventListener( Event.COMPLETE , imageStreamComplete );
//create a fresh loader instance
// this is similar to the Method 1 . Only thing is that the byte Array has not been obtained till now.
loader = new Loader();
imageCanvas.rawChildren.addChild( loader );
}
public function imageStreamProgress( event:Event ):void
{
// if there are no bytes do nothing
if( imageStream.bytesAvailable == 0 ) return
// ooo bytes process the image data
this.processImageData();
}
public function imageStreamComplete( event:Event ):void
{
// if connected, stop that.
if ( imageStream.connected ) imageStream.close();
// lets refresh the displayList after rendering cycle
imageCanvas.callLater( this.processImageData );
}
public function processImageData():void
{
// if connected, read all the bytes that have been loaded into the aggregate bytearray. The byte Array is filled with the incoming data
if ( imageStream.connected ) imageStream.readBytes( imageData , imageData.length );
// clean up the loader
loader.unload();
//push the aggregate bytearray of loaded image data in there.
loader.loadBytes( imageData );
}
public function loadImage( input:String ):void
{
//if connected we need to stop that
if ( imageStream.connected ) imageStream.close();
//lets load a new image url
imageStream.load( new URLRequest( input + '?' + getTimer() ) );
// clean out all the crud in that loader
loader.unload();
//create a fresh bytearray to store aggregate image data
imageData = new ByteArray();
}
]]>
</mx:Script>
<!-- this is the place that we are accessing the jpg image from our server-->
<mx:Button click="loadImage('http://sampleserver/scripts/sample.jpg')" y="10" label="Load sample" x="371" />
<mx:Canvas id="imageCanvas" left="10" top="40" right="10" bottom="10"/>
</mx:Application>
A rough algorithm
1. Create a URLStream to access the remote image URL
2. Create a loader object
3. Create a byte Array
4. Create a canvas or some container where to render the image. Associate the loader object as a child of this canvas
5. Listen to the events ProgressEvent.PROGRESS,Event.COMPLETE. These will help in knowing if the download is over or still progressing
6. We load the URL to the URLStream
7. Clear the loader
8. initialize the ByteArray
9. Now listen to the events.
If the Progress event occurs then
10. Read the bytes into the byteArray and then load the bytes to the loader. So we will see the some portion of the image on the client
If the Complete Event occurs then
11 close the URLStream.
Monday, January 7, 2008
Webservices
The web service can be easily used in the Flex 2. The web service may be in any language. The client can be made to consume the web service. There are two main ways to do that.
First method
<mx:WebService id="WShello" wsdl="http://localhost:2091/CreditCard/CreditCard.asmx?wsdl">
<mx:operation
name="HelloWorld" result="typeRPCResult(event)">
</mx:operation>
<mx:operation
name="ListFiles" result="showFiles(event)">
</mx:operation>
</mx:WebService>
In this case we make use of mx:webservice Tag. We need to specify the location of the WSDL file.
Then we specify the various operations we want to perform. My advice is mention only the functions that don’t want any parameter to be passed.
Result specify the function to be called once the data has arrived.
The event is of resultEvent type
Now in order to get the data we need to call the webservice. This is done by
WShello.HelloWorld.send();
i.e. Webservice_object. Operation_name. Send();
Once the result has arrived the general prototype of the function called is
Private function function_name(evt:resultEvent):void
{
//here you should typecast the event.result into the expected type
var s:string=(event.result as String);
}
Second Method
This method uses more of a programmatic approach. The main use of this method is to call the webmethods that require paramters to be passed to them
The various steps to be followed are:
1. declare an object of type websevice
public var webse:WebService = new WebService();
2. Call a function that will initialize the object
public function callWebMethod():void{
3. Inside the function you need to specify the wsdl file
webse.wsdl="http://localhost:2091/CreditCard/CreditCard.asmx?wsdl";
4. load the wsdl
webse.loadWSDL();
5. Once loaded wsdl file will generate a LoadEvent. We need to call a function on that event so that the necessary webmethod can be called
webse.addEventListener(LoadEvent.LOAD, onWSDL);
onWSDL is the method to be called
6. private function onWSDL(event:LoadEvent):void{
is the prototype for the function. Note that the event type is LoadEvent. The firast parameter in the eventListener call will specify the kind of event that will happen.
7. Before actually calling the webmethod register an helper method that will handle the results arrived from the webmethod
webse.GetFile.addEventListener(ResultEvent.RESULT, onGetFile);
specify the webmethod_name.addEventListener
Note that the event type is ResultEvent
8. Call the webmethod passing the correct parameter
Webse.GetFile(parm1.parma2…)
9. Handle the result in the onGetFile() function
Private function onGetFile(event:ResultEvent):void{
}
10. type cast the event.result to the type you require
var s:string=(event.result as String);
In my sample code I have typecasted it to an object, since the webservice will be returning a complex data type
var ob:Object=(event.result as Object);
The object is then used to populate WebFileData object.
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" creationComplete="init()">
<mx:Script>
<![CDATA[
import mx.controls.Alert;
import mx.rpc.soap.LoadEvent;
import mx.rpc.events.ResultEvent;
import mx.collections.ArrayCollection;
import valueObject.*;
[Bindable]
private var arc:ArrayCollection = new ArrayCollection();
private var theWF:WebFileData;
public var webse:WebService = new WebService();
//convert the result into the type that is returned.
public function typeRPCResult(evt:ResultEvent):void{
hell.text=(evt.result as String);
}
// what ever is returned convert the result into that.
public function showFiles(evt:ResultEvent):void{
arc=(evt.result as ArrayCollection);
}
//function to initialise the webservice (THE SECOND METHOD)
public function callWebMethod():void{
webse.wsdl="http://localhost:2091/CreditCard/CreditCard.asmx?wsdl";
webse.loadWSDL();
// the eventhandler that will be called when the loadevent happebns
webse.addEventListener(LoadEvent.LOAD, onWSDL);
}
/**
* function to display the results from the web service
**/
private function onWSDL(event:LoadEvent):void{
Alert.show("wsdl loaded");
// register the eventhandler that will be called once the result is back
webse.GetFile.addEventListener(ResultEvent.RESULT, onGetFile);
var tempString:String=txtFile.text;
var tempFileName:String;
//fileName = fileName.substr(fileName.lastIndexOf(“/”) + 1);
tempFileName=tempString.substr(tempString.lastIndexOf("\")+1);
hell.text=tempFileName;
//Alert.show(hell.text);
webse.GetFile(tempFileName);
}
// this function will be responsible to render the result on the panel
private function onGetFile(event:ResultEvent):void
{
var ob:Object=(event.result as Object);
//you dont need to specify new here , since the createWF function will take care of it
theWF=WebFileData.createWF(ob);
//hell.text=theWF.CreationTime.date.toString();
resName.text=theWF.Name;
resContent.text=theWF.Contents.toString();
resCD.text=theWF.CreationTime.toDateString()+" "+theWF.CreationTime.toTimeString();
resLAT.text=theWF.LastAccessTime.toDateString()+" "+theWF.LastAccessTime.toTimeString();
resLWT.text=theWF.LastWriteTime.toDateString()+" "+theWF.LastWriteTime.toTimeString();
}
// to invoke the web methods
private function init():void{
WShello.HelloWorld.send();
WShello.ListFiles.send();
}
]]>
</mx:Script>
<!-- this is one of the ways to define the web services (THE FIRST METHOD) -->
<mx:WebService id="WShello" wsdl="http://localhost:2091/CreditCard/CreditCard.asmx?wsdl">
<mx:operation name="HelloWorld" result="typeRPCResult(event)">
</mx:operation>
<mx:operation name="ListFiles" result="showFiles(event)">
</mx:operation>
</mx:WebService>
<mx:HBox>
<mx:VBox>
<mx:Label id="hell" />
<mx:List id="ls" dataProvider="{arc}" rowCount="{arc.length}" />
</mx:VBox>
</mx:HBox>
<mx:Panel x="221" y="10" width="500">
<mx:Label text="You have selected:"/>
<mx:Text id="txtFile" width="400" text="{ls.selectedItem.toString()}"/>
<mx:Button name="btnGet" label="Copy" click="callWebMethod()" />
</mx:Panel>
<mx:Panel x="221" y="114" width="500" height="268" layout="absolute" title="The Result">
<mx:Canvas x="10" y="0" width="100%" height="100%">
<mx:Label x="10" y="26" text="Name:"/>
<mx:Text x="60" y="26" width="213" height="18" id="resName" text="xx"/>
<mx:Label x="10" y="52" text="Content"/>
<mx:TextArea x="67" y="51" width="248" height="43" id="resContent"/>
<mx:Label x="10" y="117" text="Creation Date"/>
<mx:Text x="109" y="117" width="206" id="resCD" height="18"/>
<mx:Label x="10" y="143" text="LastAccessTime" width="93"/>
<mx:Text x="121" y="141" width="180" id="resLAT" height="20"/>
<mx:Label x="10" y="169" text="LastWriteAccess" width="93"/>
<mx:Text x="131" y="169" width="170" id="resLWT" height="18"/>
</mx:Canvas>
</mx:Panel>
</mx:Application>
/*****************************/
The WebFileData class
import flash.utils.ByteArray;
import mx.rpc.soap.*;
import mx.charts.chartClasses.DataDescription;
[Bindable]
public class WebFileData
{
public var Name:String;
public var Contents:ByteArray;
public var CreationTime:Date;
public var LastAccessTime:Date;
public var LastWriteTime:Date;
public function WebFileData(_Name:String,_Contents:ByteArray,_CreationTime:Date,_LastAccessTime:Date,_LastWriteTime:Date){
this.Name=_Name;
this.Contents=_Contents;
this.CreationTime=_CreationTime;
this.LastAccessTime=_LastAccessTime;
this.LastWriteTime=_LastWriteTime;
}
/**
* The function will accept the parameter of type object and then it will fill the datastructure and
* return back the object
**/
public static function createWF(o:Object):WebFileData{
var WF:WebFileData=new WebFileData(o.Name,o.Contents,o.CreationTime,o.LastAccessTime,o.LastWriteTime);
return WF;
}
}