Shaun Mccran

My digital playground

05
A
P
R
2009

Flex Currency Formatter Example

Whilst developing a new Flex shopping cart I came across an issue in dealing with the formatting of currencies for the payment system. The previous version of the shopping cart had preformatted values stored in the actual database, so I was simply displaying them in the Flex front end.

In this version there is the specific requirement that it is able to handle a multi currency configuration. So I thought I'd dig out the documentation on the Flex currency Formatter.

Firstly create a CurrencyFormatter object, and specify the currency symbol, and any other formatting parameters that you require.

Next create a NumberValidator, as this will validate our entered value as a numeric value, we don't need to try and re-format alpha numeric values.

Then create a form to run the test validation against. I often find it easier to build a test mechanism at the same time. Below is a simple submission form, in the form we just enter the value, click the Button, which fires the Format() function.

view plain print about
1<?xml version="1.0" encoding="utf-8"?>
2<!-- Simple example to demonstrate the CurrencyFormatter. -->
3<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute">
4
5 <mx:CurrencyFormatter id="ukFormatter" precision="2"
6 currencySymbol="�" decimalSeparatorFrom="."
7 decimalSeparatorTo="." useNegativeSign="true"
8 useThousandsSeparator="true" alignSymbol="left"/
>

9
10 <mx:NumberValidator id="numVal" source="{priceUK}" property="text"
11 allowNegative="true" domain="real"/
>

12
13 <mx:Panel title="CurrencyFormatter Example" width="75%" height="75%"
14 paddingTop="10" paddingLeft="10" paddingRight="10" paddingBottom="10"
>

15
16 <mx:Form>
17 <mx:FormItem label="Enter U.K. dollar amount:">
18 <mx:TextInput id="priceUK" text="" width="50%"/>
19 </mx:FormItem>
20
21 <mx:FormItem label="Formatted amount: ">
22 <mx:TextInput id="formattedUKPrice" text="" width="50%" editable="false"/>
23 </mx:FormItem>
24
25 <mx:FormItem>
26 <mx:Button label="Validate and Format" click="Format();"/>
27 </mx:FormItem>
28 </mx:Form>
29
30 </mx:Panel>
31
32</mx:Application>

The Format() function checks that the value is numeric, and applies the CurrencyFormatter object to the value. Then we simply assign the newly formatted value back to our Text field in the form.

view plain print about
1<mx:Script>
2 <![CDATA[
3
4 import mx.events.ValidationResultEvent;
5 private var vResult:ValidationResultEvent;
6
7 // Event handler to validate and format input.
8
private function Format():void {
9
10 vResult = numVal.validate();
11
12 if (vResult.type==ValidationResultEvent.VALID) {
13 var temp:Number=Number(priceUK.text);
14 formattedUKPrice.text= ukFormatter.format(temp);
15 }
16
17 else {
18 formattedUKPrice.text="";
19 }
20 }
21 ]]>
22 </mx:Script>

03
A
P
R
2009

AIR Phone Book application - Part 3 (Full code)

Below is the full code in one run for the PhoneBook AIR application.

view plain print about
1<?xml version="1.0" encoding="utf-8"?>
2<mx:WindowedApplication xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" initialize="init()"
3    height="280" width="294" horizontalAlign="center" verticalAlign="middle" showFlexChrome="false"
>

4
5<mx:Script>
6    <![CDATA[
7    
8     import mx.controls.Alert;
9 import mx.collections.*;
10 import mx.rpc.events.FaultEvent;
11 import mx.rpc.events.ResultEvent;
12 import mx.collections.ArrayCollection;
13
14     [Bindable]
15     private var loadedData:ArrayCollection;
16
17 public function init():void
18 {
19        // start the move listener
20
        moveListener()
21     // get the remote data
22
    getData()
23 }
24
25 public function moveListener():void
26 {
27        // mover event
28
        outerCanvas.addEventListener( MouseEvent.MOUSE_DOWN, moveWindow );     
29 }
30
31 public function getData():void
32 {
33     popData.getData();
34 }
35
36     public function moveWindow( event:MouseEvent ):void
37     {
38        var str:String = event.target.valueOf();
39
40         // if its a datagrid then don't do the move
41
         if ( str.search("displayPeople") >= 1)
42         {
43             // Do nothing
44
         }
45         else
46         {
47             stage.nativeWindow.startMove();
48         }
49     }
50    
51     public function onMinimize():void
52     {
53         stage.nativeWindow.minimize();
54     }
55    
56     public function onClose():void
57     {
58         stage.nativeWindow.close();
59     }
60
61     public function resultsHandler(event:ResultEvent):void
62     {
63        // trace(event.result)
64
        displayPeople.dataProvider = popData.getData.lastResult
65     }
66
67     public function faultHandler(event:FaultEvent):void
68     {
69        Alert.show("Error: " + event.fault.faultString, "Application Error");
70     }
71
72     public function changeImage(img:String):void
73     {
74         userImage.visible = true
75         userImage.source = "http://www.url.co.uk/wld/phonebook/" + img
76
     }
77
78 ]]>
79</mx:Script>
80
81<mx:Style>

82    .header {color: #70c7f1;}
83    .greyHeader {color: #777879;}
84    .controls {color: #000000; font-size: 13pt;}
85</mx:Style>

86
87     <mx:WebService id="popData" wsdl="http://www.url.co.uk/wld/services/phoneBook.cfc?wsdl" showBusyCursor="true" useProxy="false">

88 <mx:operation name="getData" fault="faultHandler(event)" result="resultsHandler(event)" />
89 </mx:WebService>
90
91     <mx:Fade id="fadeOut" duration="1.0" alphaFrom="1.0" alphaTo="0.0"/>
92 <mx:Fade id="fadeIn" duration="2000" alphaFrom="0.0" alphaTo="1.0"/>
93
94    <mx:Canvas id="outerCanvas" x="0" y="0" width="220" height="240" backgroundColor="#70c7f1" borderStyle="solid" cornerRadius="25" borderThickness="0">
95    
96        <mx:Canvas id="innerCanvas" x="10" y="22" width="200" height="210" backgroundColor="#FFFFFF" borderStyle="solid" cornerRadius="25" borderThickness="0">
97            
98            <mx:Label x="10" y="10" text="White label" id="header" styleName="header" fontWeight="bold"/>
99            <mx:Label x="78" y="10" text="Dating PhoneBook" styleName="greyHeader" fontWeight="bold"/>
100            <mx:DataGrid id="displayPeople" x="10" y="32" width="180" height="108" itemClick="changeImage(displayPeople.selectedItem.IMAGE)">
101                <mx:columns>
102                    <mx:DataGridColumn headerText="Name" width="140" dataField="NAME"/>
103                    <mx:DataGridColumn headerText="No." width="40" dataField="NO"/>
104                    <mx:DataGridColumn headerText="Img" width="40" dataField="IMAGE" visible="false"/>
105                </mx:columns>
106            </mx:DataGrid>
107            <mx:Image x="138" y="150" source="@Embed(source='wldLogoTiny.png')" />
108            <mx:Image x="25" y="144" toolTip="{displayPeople.selectedItem.NAME}" id="userImage" visible="true" showEffect="{fadeIn}" />
109        </mx:Canvas>
110        <mx:Label text="_" styleName="controls" toolTip="Minimize" x="173" y="-2" click="onMinimize()" />
111        <mx:Label text="X" styleName="controls" toolTip="Close" x="184" y="1" click="onClose()" />
112
113    </mx:Canvas>
114
115</mx:WindowedApplication>

PhoneBook screen shot

03
A
P
R
2009

AIR Phone Book application - Part 2 (Functions and WebService)

As our application starts I want to fire the request for data, so we call an init() method on initialization. Also I have turned the flex chrome off here with 'showFlexChrome="false"'.

The init() method calls an event listener that controls the window movement (Drag and drop) and makes a call to getData().

view plain print about
1import mx.controls.Alert;
2 import mx.collections.*;
3 import mx.rpc.events.FaultEvent;
4 import mx.rpc.events.ResultEvent;
5 import mx.collections.ArrayCollection;
6
7     [Bindable]
8     private var loadedData:ArrayCollection;
9 public function init():void
10 {
11        // start the move listener
12        moveListener()
13     // get the remote data
14     getData()
15 }

The moveListener() method adds a listener to the outerCanvas element which forms the application 'border'. When this event is fired it calls moveWindow.

The getData() function calls the webservice, and specifies which method to call.

view plain print about
1public function moveListener():void
2 {
3        // mover event
4        outerCanvas.addEventListener( MouseEvent.MOUSE_DOWN, moveWindow );     
5 }
6
7 public function getData():void
8 {
9     popData.getData();
10 }
11     public function moveWindow( event:MouseEvent ):void
12     {
13        var str:String = event.target.valueOf();
14
15         // if its a datagrid then don't do the move
16         if ( str.search("displayPeople") >
= 1)
17         {
18             // Do nothing
19         }
20         else
21         {
22             stage.nativeWindow.startMove();
23         }
24     }

The moveWindow function also contains a check to see if the datagrid was the event target, as this was interfering with the functionality of the Datagrid. It would be interesting to see if anyone else has a more elegant solution to this, rather than a specific element check.

To populate our datagrid we need to use a data provider. In FLEX applications I usually use the RemoteObject function, but for AIR I've been using the WebService tag.

view plain print about
1<mx:WebService id="popData" wsdl="http://url/wld/services/phoneBook.cfc?wsdl" showBusyCursor="true" useProxy="false">
2 <mx:operation name="getData" fault="faultHandler(event)" result="resultsHandler(event)" />
3 </mx:WebService>

The final two 'chrome' functions we need are the minimize and close functions. I will detail handling custom chrome in another article.

view plain print about
1public function onMinimize():void
2     {
3         stage.nativeWindow.minimize();
4     }
5    
6     public function onClose():void
7     {
8         stage.nativeWindow.close();
9     }

Our WebService is referencing two functions. A results handler and a fault handler.

view plain print about
1public function resultsHandler(event:ResultEvent):void
2     {
3        // trace(event.result)
4        displayPeople.dataProvider = popData.getData.lastResult
5     }
6
7     public function faultHandler(event:FaultEvent):void
8     {
9        Alert.show("Error: " + event.fault.faultString, "Application Error");
10     }

The resultsHandler() simply assigns the datagrids dataprovider as the result of the WebService call. By adding DataGridColumn's to the datagrid with the right naming convention our results from the returned query object will map directly to our datagrid.

The faultHandler() function simply Alerts a user to a fault event.

Lastly I have a function that assigns the image source dynamically based on the click event in the datagrid.

view plain print about
1public function changeImage(img:String):void
2     {
3         userImage.visible = true
4         userImage.source = "http://url/wld/phonebook/" + img
5     }

So that completes the Phone Book AIR application. There are one or two tweaks I'd like to make in the image handling, but otherwise its exactly the spec I had in mind.

You can view the full code here.

03
A
P
R
2009

AIR Phone Book application - Part 1 (CFC and GUI)

I'm always asking what peoples phone numbers are in the office, we currently don't have any internal communications (like an intranet) so I thought I'd create a handy phone book application in AIR.

With FLEX of AIR applications I often wireframe them up with the data model in mind first. If you know what data you are going to display, and the format and delivery mechanism of that data, it can often have a large impact on the design and layout of your application.

In this instance I was just returning a simple query object of users and their phone numbers and a thumbnail image.

The CFC

My preferred server language is ColdFusion, so my service is a CFC object.

view plain print about
1<cfcomponent hint="WLD phoneBook" output="false">
2
3    <cffunction name="getData" access="remote" hint="Gets phoneBook data" returntype="query">
4    
5        <cfquery datasource="#application.dns#" name="qGetPB">
6            select     id AS ID,
7                    name AS Name,
8                    number AS No,
9                    image As Image
10            from phonebook
11            Order by name
12        </cfquery>
13
14        <cfreturn qGetPB>
15    </cffunction>
16    
17</cfcomponent>

In my example I'm using an MS SQL database, so I have included the creation script here:

view plain print about
1SET ANSI_NULLS ON
2GO
3SET QUOTED_IDENTIFIER ON
4GO
5SET ANSI_PADDING ON
6GO
7CREATE TABLE [dbo].[phonebook](
8    [id] [int] IDENTITY(1,1) NOT NULL,
9    [name] [varchar](20) NULL DEFAULT (NULL),
10    [number] [int] NULL DEFAULT (NULL),
11    [image] [varchar](55) NULL DEFAULT (NULL)
12) ON [PRIMARY]
13
14GO
15SET ANSI_PADDING OFF

Now that we know what the data will look like we can build the GUI front end.

My display layer is going to be a canvas, with another canvas inside it, to create a bordered effect.

Then I have a DataGrid, with a click event that will call an AS function. This will control the displaying of an image that corresponds to the user being clicked. Its always nice to see who you want to call!

view plain print about
1<mx:Fade id="fadeOut" duration="1.0" alphaFrom="1.0" alphaTo="0.0"/>
2<mx:Fade id="fadeIn" duration="2000" alphaFrom="0.0" alphaTo="1.0"/>
3    <mx:Canvas id="outerCanvas" x="0" y="0" width="220" height="240" backgroundColor="#70c7f1" borderStyle="solid" cornerRadius="25" borderThickness="0">
4    
5        <mx:Canvas id="innerCanvas" x="10" y="22" width="200" height="210" backgroundColor="#FFFFFF" borderStyle="solid" cornerRadius="25" borderThickness="0">
6            
7            <mx:Label x="10" y="10" text="White label" id="header" styleName="header" fontWeight="bold"/>
8            <mx:Label x="78" y="10" text="Dating PhoneBook" styleName="greyHeader" fontWeight="bold"/>
9            <mx:DataGrid id="displayPeople" x="10" y="32" width="180" height="108" itemClick="changeImage(displayPeople.selectedItem.IMAGE)">
10                <mx:columns>
11                    <mx:DataGridColumn headerText="Name" width="140" dataField="NAME"/>
12                    <mx:DataGridColumn headerText="No." width="40" dataField="NO"/>
13                    <mx:DataGridColumn headerText="Img" width="40" dataField="IMAGE" visible="false"/>
14                </mx:columns>
15            </mx:DataGrid>
16            <mx:Image x="138" y="150" source="@Embed(source='wldLogoTiny.png')" />
17            <mx:Image x="25" y="144" toolTip="{displayPeople.selectedItem.NAME}" id="userImage" visible="true" showEffect="{fadeIn}" />
18        </mx:Canvas>
19        <mx:Label text="_" styleName="controls" toolTip="Minimize" x="173" y="-2" click="onMinimize()" />
20        <mx:Label text="X" styleName="controls" toolTip="Close" x="184" y="1" click="onClose()" />
21
22    </mx:Canvas>

My 'userImage'has a showEffect attribute, that uses an image fadeIn method. It fades in the first image called, but not any others, I've had a play around with this, and I can't get it to fade in subsequent images, so if anyone has any ideas let me know!

Lastly I have added some chrome controls, as I will be removing the standard chrome, and building my own.

Now, on to the functions.

_UNKNOWNTRANSLATION_ /