Flex AIR AS 3 - Access dom element (textarea) from HTML component

March 4th, 2008

I’ve struggled a bit to find out how to access a textarea (and select box) that was presented in a Flex AIR HTML component.

Wasn’t much work in the end, but here’s an example (by using babelfish’s translation page) of how to access multiple elements from Flex:

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <mx:WindowedApplication xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" width="1024" height="768">
  3.  
  4. <mx:Script>
  5. <![CDATA[
  6. /**
  7. * This example describes how to access HTML elements from Flex AIR AS3.
  8. * It makes use of the babelfish translation website to access the translation elements
  9. * and then submit the specified translation to babelfish.
  10. *
  11. * @Author: S.Radovanovic
  12. * @Url: http://www.saskovic.com/blog/?p=7
  13. * @Date: 05-03-2008
  14. */
  15.  
  16. import mx.controls.Alert;
  17.  
  18. //Our access to the HTML Dom
  19. private var domWindow:Object;
  20. private var blnAlertThrown:Boolean = false;
  21.  
  22. /**
  23. * Find the corresponding element and be able to access it from Flex
  24. *
  25. */
  26. private function initDomWindow(event:Event):void {
  27. //Retrieve a link to the textarea
  28. domWindow = event.currentTarget.domWindow;
  29.  
  30. //Set a trigger (if not done yet)
  31. if( !blnAlertThrown ) {
  32. Alert.show("Click ok to set access HTML elements from Flex", "Access HTML elements", Alert.OK, this, setTranslationValues);
  33. blnAlertThrown = true; //Make sure we don't loop
  34. }
  35. }
  36.  
  37. /**
  38. * Retrieve a reference to a specific HTML element specified by name
  39. */
  40. private function getElementFromHTML(elementName:String):Object {
  41. var arrayContainingAllFoundElements:Object = domWindow.document.getElementsByName(elementName);
  42.  
  43. //We could do some checks here to see if anything was found, but I leave that up to the you... ;)
  44.  
  45. //Always return the first element (there also should only be one)
  46. return arrayContainingAllFoundElements[0];
  47. }
  48.  
  49. /**
  50. * Set the elements in the webpage to your likings.
  51. */
  52. private function setTranslationValues(event:Event):void {
  53. //Get access to the translation text area and pre-fill it
  54. var translationTextArea:Object = getElementFromHTML("trtext");
  55. translationTextArea.value = "Set my own values from Flex!";
  56.  
  57. //Loop through the possible language options of the selectbox and set the one we want (in this case english => dutch)
  58. var languageSelectbox:Object = getElementFromHTML("lp");
  59. for( var i:int = 0; i < languageSelectbox.options.length; i++ ) {
  60. trace(i + ' - ' + languageSelectbox.options[i].value);
  61. if( languageSelectbox.options[i].value == "en_nl" ) {
  62. languageSelectbox.selectedIndex = i;
  63. }
  64. }
  65.  
  66. Alert.show("Submit the values for translation by babelfish", "Translate", Alert.OK, this, submitTranslation);
  67. }
  68.  
  69. /**
  70. * Hit submit!
  71. */
  72. public function submitTranslation(event:Event):void {
  73. //And finally hit translate!
  74. var submitButton:Object = getElementFromHTML("btnTrTxt");
  75. submitButton.click();
  76.  
  77. }
  78. ]]>
  79. </mx:Script>
  80.  
  81. <mx:HTML id="htmlTranslate" x="10" y="84" location="http://babelfish.altavista.com/" complete="initDomWindow(event)"/>
  82.  
  83. </mx:WindowedApplication>

Download code

Flex 3 Air - Minimize to system tray (WindowedApplication)

November 24th, 2007

I’ve been waiting for a release of Flex (AIR) where system tray (systray) options where available and finally with the latest release of Adobe AIR it’s there.

After searching some while on the internet I’ve found my way of minimizing my AIR application to the system tray, including a little menu.
Important to understand is that I wanted a minimize solution for a WindowedApplication. The WindowApplication offers it’s own minimize, maximize and close buttons, so these actions have to be caught if we want to attach our own actions to them.

Flex offers you docking, so that your application can be minimized, this is available for Windows and Mac platforms. The example presented here is for Windows, but close to what it should be for Macs.

I’ve created an Systray test application which contains the basic code, necessary for docking and undocking your application from the systray. The only part you will not need is the dummy label…
To test this you can:
1. create a new Flex project (application type AIR) called Systray.
2. edit Systray-app.mxml and set SystemChrome to none (so that you get the cool app skin ;) ).
2. copy the contents of the presented code into the Systray.mxml.

After testing it (and also without :) ), you should be able to place the code into your own project and use it.

Here’s the code:

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <mx:WindowedApplication xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" creationComplete="initApplication()">
  3. <mx:Script>
  4. <![CDATA[
  5. /**
  6. * This example describes how to dock an AIR application to the system tray
  7. * and then undock it again.
  8. * The minimize and close actions of the WindowedApplication are caught, so
  9. * that we can introduce our own actions.
  10. *
  11. * A simple systray menu is presented, to show the usage of that.
  12. *
  13. * @Author: S.Radovanovic
  14. * @Url: http://www.saskovic.com/blog/?p=5
  15. * @Date: 24-11-2007
  16. * @Author: Jeffry Houser
  17. * @Date: 09-01-2007
  18. * Updated for AIR Beta 3
  19. */
  20.  
  21. import mx.controls.Alert;
  22. import mx.events.CloseEvent;
  23.  
  24. private var dockImage:BitmapData;
  25.  
  26. /**
  27. * Initialize the application to the default values.
  28. * This method is called upon creationComplete from the Windowed Application
  29. *
  30. * @Author: S.Radovanovic
  31. */
  32. public function initApplication():void {
  33.  
  34. //Use the loader object to load an image, which will be used for the systray //After the image has been loaded into the object, we can prepare the application //for docking to the system tray
  35. var loader:Loader = new Loader();
  36. loader.contentLoaderInfo.addEventListener(Event.COMPLETE, prepareForSystray);
  37. loader.load(new URLRequest("http://www.saskovic.com/images/systray_icon_16.png"));
  38.  
  39. //Catch the closing event so that the user can decide if it wants to dock or really //close the application
  40. this.addEventListener(Event.CLOSING, closingApplication);
  41. }
  42.  
  43. /**
  44. * Check if the user wants to close the application or dock it
  45. *
  46. * @Author: S.Radovanovic
  47. */
  48. private function closingApplication(evt:Event):void {
  49. //Don't close, so prevent the event from happening
  50. evt.preventDefault();
  51.  
  52. //Check what the user really want's to do //Alert.buttonWidth = 110;
  53. Alert.yesLabel = "Close";
  54. Alert.noLabel = "Minimize";
  55. Alert.show("Close or minimize?", "Close?", 3, this, alertCloseHandler);
  56. }
  57.  
  58. // Event handler function for displaying the selected Alert button.
  59. private function alertCloseHandler(event:CloseEvent):void {
  60. if (event.detail==Alert.YES) {
  61. closeApp(event);
  62. } else {
  63. dock();
  64. }
  65. }
  66.  
  67.  
  68. /**
  69. * Check to see if the application may be docked and set basic properties
  70. *
  71. * @Author: S.Radovanovic
  72. */
  73.  
  74. public function prepareForSystray(event:Event):void {
  75.  
  76. //Retrieve the image being used as the systray icon
  77. dockImage = event.target.content.bitmapData;
  78.  
  79. //For windows systems we can set the systray props //(there's also an implementation for mac's, it's similar and you can find it on the net... ;) )
  80. if (NativeApplication.supportsSystemTrayIcon){
  81. setSystemTrayProperties();
  82.  
  83. //Set some systray menu options, so that the user can right-click and access functionality //without needing to open the application
  84. SystemTrayIcon(NativeApplication.nativeApplication .icon).menu = createSystrayRootMenu();
  85. }
  86. }
  87.  
  88. /**
  89. * Create a menu that can be accessed from the systray
  90. *
  91. * @Author: S.Radovanovic
  92. */
  93. private function createSystrayRootMenu():NativeMenu{
  94. //Add the menuitems with the corresponding actions
  95. var menu:NativeMenu = new NativeMenu();
  96. var openNativeMenuItem:NativeMenuItem = new NativeMenuItem("Open");
  97. var exitNativeMenuItem:NativeMenuItem = new NativeMenuItem("Exit");
  98.  
  99. //What should happen when the user clicks on something...
  100.  
  101. openNativeMenuItem.addEventListener(Event.SELECT, undock);
  102.  
  103. exitNativeMenuItem.addEventListener(Event.SELECT, closeApp);
  104.  
  105. //Add the menuitems to the menu
  106. menu.addItem(openNativeMenuItem);
  107. menu.addItem(new NativeMenuItem("",true));
  108. //separator
  109. menu.addItem(exitNativeMenuItem);
  110.  
  111. return menu;
  112. }
  113.  
  114. /**
  115. * To be able to dock and undock we need to set some eventlisteners
  116. *
  117. * @Author: S.Radovanovic
  118. */
  119. private function setSystemTrayProperties():void{
  120. //Text to show when hovering of the docked application icon
  121. SystemTrayIcon(NativeApplication.nativeApplication .icon).tooltip = "Systray test application";
  122.  
  123. //We want to be able to open the application after it has been docked
  124. SystemTrayIcon(NativeApplication.nativeApplication .icon).addEventListener(MouseEvent.CLICK, undock);
  125.  
  126. //Listen to the display state changing of the window, so that we can catch the minimize
  127. stage.nativeWindow.addEventListener(NativeWindowDisplayStateEvent.DISPLAY_STATE_CHANGING, nwMinimized); //Catch the minimize event
  128. }
  129.  
  130. /**
  131. * Do the appropriate actions after the windows display state has changed.
  132. * E.g. dock when the user clicks on minize
  133. *
  134. * @Author: S.Radovanovic
  135. */
  136. private function nwMinimized(displayStateEvent:NativeWindowDisplayStateEvent):void {
  137.  
  138. //Do we have an minimize action? //The afterDisplayState hasn't happened yet, but only describes the state the window will go to, //so we can prevent it!
  139. if(displayStateEvent.afterDisplayState == NativeWindowDisplayState.MINIMIZED) {
  140. //Prevent the windowedapplication minimize action from happening and implement our own minimize //The reason the windowedapplication minimize action is caught, is that if active we're not able to //undock the application back neatly. The application doesn't become visible directly, but only after clicking //on the taskbars application link. (Not sure yet what happens exactly with standard minimize)
  141. displayStateEvent.preventDefault();
  142.  
  143. //Dock (our own minimize)
  144. dock();
  145. }
  146. }
  147.  
  148. /**
  149. * Do our own 'minimize' by docking the application to the systray (showing the application icon in the systray)
  150. *
  151. * @Author: S.Radovanovic
  152. */
  153. public function dock():void {
  154. //Hide the applcation
  155. stage.nativeWindow.visible = false;
  156.  
  157. //Setting the bitmaps array will show the application icon in the systray
  158. NativeApplication.nativeApplication .icon.bitmaps = [dockImage];
  159. }
  160.  
  161. /**
  162. * Show the application again and remove the application icon from the systray
  163. *
  164. * @Author: S.Radovanovic
  165. */
  166. public function undock(evt:Event):void {
  167. //After setting the window to visible, make sure that the application is ordered to the front, //else we'll still need to click on the application on the taskbar to make it visible
  168. stage.nativeWindow.visible = true;
  169. stage.nativeWindow.orderToFront();
  170.  
  171. //Clearing the bitmaps array also clears the applcation icon from the systray
  172. NativeApplication.nativeApplication .icon.bitmaps = [];
  173. }
  174.  
  175.  
  176. /**
  177. * Close the application
  178. *
  179. * @Author: S.Radovanovic
  180. */
  181. private function closeApp(evt:Event):void {
  182. stage.nativeWindow.close();
  183. }
  184. ]]>
  185. </mx:Script>
  186.  
  187. </mx:WindowedApplication>

Download code

Tips on shortening some code or something else are always welcome.

Thanks for Jeffry Houser for updating it for Flex 3 (beta 3).

Flex 2 - Export Datagrid To Excel

March 7th, 2007

I recently came across the problem of exporting Datagrid data to an Excel sheet.

Research on the internet brought me to a post on cflex where an solution was presented for Flex < 2.
Together with a (half-visual) comment posted there I refactored the code so that it would be applicable to Flex 2 (And didn’t need to work through the clipboard, since in my opinion this would work only half the time (browser security)).

The following Actionscript code is used for converting the Datagrid to an Excel file and then passing it on to an external Excel exporter:

  1. <mx:Script>
  2. <![CDATA[
  3. /**
  4. * Simple script to convert a Datagrid to a HTML table and then
  5. * pass it on to an external excel exporter
  6. *
  7. * @author: S.Radovanovic (With help of Tracy Spratt through the post on
  8. * http://www.cflex.net/showFileDetails.cfm?ObjectID=298&Object=File&ChannelID=1)
  9. */
  10.  
  11. //Libs that are mostly used
  12. //(only a number are necessary for the datagrid conversion and export)
  13. import mx.controls.Alert;
  14. import mx.core.UIComponent;
  15. import mx.core.Container;
  16. import mx.events.ItemClickEvent;
  17. import mx.utils.ObjectProxy;
  18. import flash.errors.*;
  19. import flash.events.*;
  20. import flash.external.*;
  21. import flash.net.URLLoader;
  22. import flash.net.URLVariables;
  23. import flash.net.URLRequest;
  24.  
  25. //The location of the excel export file
  26. public var urlExcelExport:String = "/yourpath/excelexport.php";
  27.  
  28.  
  29. /**
  30. * Convert the datagrid to a html table
  31. * Styling etc. can be done externally
  32. *
  33. * @param: dg Datagrid Contains the datagrid that needs to be converted
  34. * @returns: String
  35. */
  36. private function convertDGToHTMLTable(dg:DataGrid):String {
  37. //Set default values
  38. var font:String = dg.getStyle('fontFamily');
  39. var size:String = dg.getStyle('fontSize');
  40. var str:String = '';
  41. var colors:String = '';
  42. var style:String = 'style="font-family:'+font+';font-size:'+size+'pt;"';
  43. var hcolor:Array;
  44.  
  45. //Retrieve the headercolor
  46. if(dg.getStyle("headerColor") != undefined) {
  47. hcolor = [dg.getStyle("headerColor")];
  48. } else {
  49. hcolor = dg.getStyle("headerColors");
  50. }
  51.  
  52. //Set the htmltabel based upon knowlegde from the datagrid
  53. str+= '<table width="'+dg.width+'"><thead><tr width="'+dg.width+'" style="background-color:#' +Number((hcolor[0])).toString(16)+'">';
  54.  
  55. //Set the tableheader data (retrieves information from the datagrid header
  56. for(var i:int = 0;i<dg.columns.length;i++) {
  57. colors = dg.getStyle("themeColor");
  58.  
  59. if(dg.columns[i].headerText != undefined) {
  60. str+="<th "+style+">"+dg.columns[i].headerText+"</th>";
  61. } else {
  62. str+= "<th "+style+">"+dg.columns[i].dataField+"</th>";
  63. }
  64. }
  65. str += "</tr></thead><tbody>";
  66. colors = dg.getStyle("alternatingRowColors");
  67.  
  68. //Loop through the records in the dataprovider and
  69. //insert the column information into the table
  70. for(var j:int =0;j<dg.dataProvider.length;j++) {
  71. str+="<tr width=\""+Math.ceil(dg.width)+"\">";
  72.  
  73. for(var k:int=0; k < dg.columns.length; k++) {
  74.  
  75. //Do we still have a valid item?
  76. if(dg.dataProvider.getItemAt(j) != undefined && dg.dataProvider.getItemAt(j) != null) {
  77.  
  78. //Check to see if the user specified a labelfunction which we must
  79. //use instead of the dataField
  80. if(dg.columns[k].labelFunction != undefined) {
  81. str += "<td width=\""+Math.ceil(dg.columns[k].width)+"\" "+style+">"+dg.columns[k].labelFunction(dg.dataProvider.getItemAt(j),dg.columns[k].dataField)+"</td>";
  82.  
  83. } else {
  84. //Our dataprovider contains the real data
  85. //We need the column information (dataField)
  86. //to specify which key to use.
  87. str += "<td width=\""+Math.ceil(dg.columns[k].width)+"\" "+style+">"+dg.dataProvider.getItemAt(j)[dg.columns[k].dataField]+"</td>";
  88. }
  89. }
  90. }
  91. str += "</tr>";
  92. }
  93. str+="</tbody></table>";
  94.  
  95. return str;
  96. }
  97.  
  98. /**
  99. * Load a specific datagrid into Excel
  100. * This method passes the htmltable string to an backend script which then
  101. * offers the excel download to the user.
  102. * The reason for not using a copy to clipboard and then javascript to
  103. * insert it into Excel is that this mostly will fail because of the user
  104. * setup (Webbrowser configuration).
  105. *
  106. * @params: dg Datagrid The Datagrid that will be loaded into Excel
  107. */
  108. private function loadDGInExcel(dg:DataGrid):void {
  109.  
  110. //Pass the htmltable in a variable so that it can be delivered
  111. //to the backend script
  112. var variables:URLVariables = new URLVariables();
  113. variables.htmltable = convertDGToHTMLTable(dg);
  114.  
  115. //Setup a new request and make sure that we are
  116. //sending the data through a post
  117. var u:URLRequest = new URLRequest(urlExcelExport);
  118. u.data = variables; //Pass the variables
  119. u.method = URLRequestMethod.POST; //Don't forget that we need to send as POST
  120.  
  121. //Navigate to the script
  122. //We can use _self here, since the script will through a filedownload header
  123. //which results in offering a download to the user (and still remaining in you Flex app.)
  124. navigateToURL(u,"_self");
  125. }
  126. ]]>
  127. </mx:Script>

Download code

The code to export the data to Excel is fairly simple.
I’ve written it in PHP, but it can be written in any language.

  1. <?php
  2. /**
  3. * Export data, delivered in the POST, to excel.
  4. *
  5. * @author S.Radovanovic
  6. * @version $Id$
  7. */
  8. header('ETag: etagforie7download'); //IE7 requires this header
  9. header('Content-type: application/octet_stream');
  10. header('Content-disposition: attachment; filename="rapportage.xls"');
  11.  
  12. //Add html tags, so that excel can interpret it
  13. echo "<html>
  14. <body>
  15. ".stripslashes($_POST["htmltable"])."
  16. </body>
  17. </html>
  18. ";
  19. ?>

Download code