Article

Ajaxify Your Flex Application

Page: 1 2

ExternalInterface

Now we need to introduce a truly bidirectional communication channel between the Flex application and its wrapper: Flash Player's external API, also known as ExternalInterface. The ExternalInterface class is the representation of the external API in ActionScript 3. In earlier versions of Flash (up to version 8) the fscommand function was used to provide access to the external API.

It can be applied in the following use-cases:

  • retrieve information about the Flex application's container
  • call and execute code in the container from ActionScript in Flex
  • call and execute ActionScript code in the Flex application from the container

The external API is a subsystem of the Flash Player that's being leveraged in ActionScript 3. When I mentioned "call and execute code in the container" in the list above, I was actually referring to executing JavaScript code in our HTML wrapper page. We will therefore introduce a way to set up and run bidirectional function calls from HTML and JavaScript to Flex and ActionScript.

Before I show you some code, let's talk about stuff that’s good to know:


  1. First, some bad news: you may sometimes struggle to have access to the ExternalInterface class. The good news is, though, that you rarely run into issues regarding the availability of the ExternalInterface on most of the modern browsers. You'll be fine if the client browser is one of either:

    • Internet Explorer 5+ on Windows
    • a browser supporting the NPRuntime interface (for example, Firefox 1+, Safari 1.3+, Netscape 8+, Opera 9+)

    The recommended way to check the availability of the external interface is to test the value of ExternalInterface.available; it will return true if available. This result actually neglects telling you about the status of the browser's JavaScript settings (that is, whether the user’s JavaScript is enabled); it will only inform you that conceptually your application could leverage the external API.

    Keeping that in mind, you should apply the following generic structure to your external API calls in ActionScript:

    if(ExternalInterface.available)  
    {  
     // Execute ExternalInterface calls in here.  
    }


  2. If the HTML tags (the resulting <object> and <embed> tags) are nested in a HTML form, ExternalInterface calls from Flex and ActionScript to JavaScript in the HTML wrapper will fail to work.

  3. In Internet Explorer, if the <object> tag's id attribute contains a character that can be interpreted as a JavaScript operator (for example, -) Flex/ActionScript calls to the wrapper will function incorrectly.

Calling JavaScript Code from Flex

I’ll demonstrate the first use-case I mentioned previously: retrieving container information. Let's have a look at a Flex application using the ExternalInterface class to display the navigator.userAgent property of its surrounding container:

<?xml version="1.0" encoding="utf-8"?>  
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="vertical" verticalAlign="middle" creationComplete="init();">  
 
 <mx:Script>  
   <![CDATA[  
     import mx.controls.Alert;  
 
     private function init():void  
     {  
       if (ExternalInterface.available)  
       {  
         var userAgent:String = ExternalInterface.call(  
             "navigator.userAgent.toString");  
          Alert.show(userAgent, "UserAgent information:");  
        }  
      }  
    ]]>  
  </mx:Script>  
 
 <mx:Button label="display user agent (again)" click="init();" />  
 
</mx:Application>

The simple logic is encapsulated in the init function, and we can see that ExternalInterface.call actually executes the call to the HTML wrapper. In this case we're simply calling the toString function for the navigator.userAgent property.

Let's take the next step: what if we had some JavaScript code on the page that we would want to execute from within our Flex application? Actually, it's fairly straightforward, with the call method of the ExternalInterface class again doing the job for us:

...  
<mx:Script>  
 <![CDATA[  
   private function callJavaScript():void  
   {  
     ExternalInterface.call("sayHelloWorld");  
   }  
 ]]>  
</mx:Script>  
...

In this instance, we're providing call with the name of the JavaScript function we wish to execute. The corresponding JavaScript function obviously has to be included in the wrapper; in the following example, we're basically triggering the display of a JavaScript alert popup from within the Flash Player. Here's the JavaScript:

<script type="text/javascript">  
 function sayHelloWorld()  
 {  
   alert("Hello World from JavaScript");  
 }  
</script>

Passing arguments from ActionScript to a JavaScript function via the ExternalInterface class follows a very similar syntax. The arguments are passed into the call method as additional parameters:

...  
<mx:Script>  
 <![CDATA[  
   private function callJavaScript():void  
   {      var a:int = 4;  
     var b:int = 4711;  
     var calcResult:int =  
         ExternalInterface.call("doCalculation",a,b);  
   }  
 ]]>  
</mx:Script>  
...

The JavaScript function might appear as below:

<script type="text/javascript">  
 function doCalculation(number1, number2)  
 {  
   return number1 * number2;  
 }  
</script>

Be aware that if the call to JavaScript fails or your JavaScript function is without an appropriate return value, the result of the ExternalInterface call would be null.

If you're dealing with security sandboxes in the Flash Player, you might experience SecurityError exceptions. There are two things you could do to avoid trouble during development, as well as when moving from development to testing and production:

  1. Set an appropriate value of the allowScriptAccess attribute in the <object> and <embed> tags of your HTML page.

  2. Develop in a realistic environment—build and test your applications in pages delivered to your browser from a (local) HTTP server such as Apache or IIS via the http:// protocol, instead of using the file:// protocol prefix in your browser.

What’s good about using the external API is that it allows us to use most plain data types, and also some complex types like Arrays.

Calling ActionScript Code from HTML and JavaScript

As I've indicated before, ExternalInterface offers a bidirectional communication channel. Therefore, we're able to call ActionScript code in the Flex application from JavaScript code embedded into the HTML wrapper. This process turns out to be a bit more complex than the other way around, because we actually will have to set up some callback handlers.

Let's again experiment with displaying alert popups. This time we want to use a HTML button as a trigger for displaying an alert window in the Flex application, which itself displays the current date and time. The first step is the callback, because we need to tell the Flex application which ActionScript method to run if a particular signal is being sent from the wrapper. The callback is created in the Flex application's init method:

<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" ... creationComplete="init();" ...>  
 
 <mx:Script>  
   <![CDATA[  
     import mx.controls.Alert;  
     private var alert:Alert;  
 
     private function init():void  
     {  
       ExternalInterface.addCallback("showAlert", showAlert);  
     }  
 
     private function showAlert(msg:String):void  
     {  
       var now:Date = new Date();  
       alert = Alert.show(msg,now.toLocaleDateString());  
       alert.status = now.toLocaleTimeString();  
     }  
 ]]>  
</mx:Script>  
 
</mx:Application>

Our use of the addCallBack method above exposes a function name that can be called by JavaScript, "showAlert"; that will then call the ActionScript method showAlert.

On the HTML page we define the function that will be executed with the click of a button, asAlert, and one other helper function needed to obtain a reference to the Flash movie object, thisMovie:

<script type="text/javascript">  
 function thisMovie(movieName)  
 {  
   if (navigator.appName.indexOf("Microsoft") != -1)  
   {  
     return window[movieName];  
   }  
   else  
   {  
     return document[movieName];  
   }  
 }  
 
 function asAlert(value)  
 {  
   thisMovie("alertFromFlex").showAlert(value);  
 }  
</script>

As you can see, the function asAlert leverages the helper function thisMovie to return a reference to the HTML DOM element containing our Flash movie, and calls its showAlert method, passing in a string value.

To make the code above work properly, you'd need to ensure this: the argument being passed into the thisMovie function (here "alertFromFlex") needs to be identical to the id attribute of the <object> tag, as well as the name attribute of the <embed> tag being used to embed your Flex application in the page.

Please be aware of one potential issue you could run into when using JavaScript to communicate with the Flex application: it's hard to predict the exact point in time during page rendering that the Flex application will be available. Therefore, it might be a good idea to set a flag named jsReady to true using the onload event of the browser window that could be queried from ActionScript. If the value of the flag is true, we can safely assume the page load has been complete and we can set up the callback handlers in ActionScript.

Where Next?

While this article should help start you off, there are plenty of other possibilities for the Flash Player's external API. Examples are unlimited, and could include complex form elements and controls built in Flex that have to be embedded into existing HTML forms; image upload controls and management systems in Flex that have to interact with the surrounding HTML page; or hooking your Flex application into existing third party JavaScript APIs.

In the article, I mentioned the possibility of having a non-HTML page wrapper. Usually this would be the Flash Player ActiveX control, embedded in a stand-alone application developed in other technologies. Adobe provides some basic information on how to approach such an undertaking with C# and .NET. A highly recommended tutorial on embedding the ActiveX control into your own applications can be found on richapps.de.

Also, if you're serious about linking Flex and JavaScript, make sure you have a further look into a library called Flex/AJAX Bridge. The name is slightly misleading, as it's really a library to make the communication between both technologies easier, with barely a focus on Ajax. It basically abstracts the underlying ExternalInterface calls, and gives you easy access to passing even complex objects—such as references to UI components—between Flex and the wrapper. Only a small amount of people know about the existence of this tool kit, but it comes with the Flex SDK and Flex Builder. You'll find it in the folder frameworks/javascript/fabridge.

Test Your Knowledge

Remember all that? Test yourself on the contents of this article by doing the quiz. Submit your answers for a chance to win a free copy of Adobe CS4 Web Premium and Flex Builder 3 Pro. Take the quiz now!

If you liked this article, share the love:
Print-Friendly Version Suggest an Article

Sponsored Links

Rate This Article

  • 1
    Poor
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
    Great

Comment on This Article

Have something to say?

Post A Comment

You need to be a member of the SitePoint Forums to comment on this post. Sign Up

Already a member? Post using your SitePoint Forums account: