Make Your Flash Forms More FLEXible

Want to make your Flash forms more FLEXible? Well, now you can! But, is there any point, you say, now that Flex 2 is out and effectively free (if you can make do without FlexBuilder)? Well, if you can go with Flex 2 then do so, but maybe like me, it's currently off limits to you...

Since a bunch of the work I'm doing right now is for Sun Microsystems, it's vital that the application runs on Solaris, and for now there's no sign of FlashPlayer 9, which is required for Flex 2, being near to release. Same goes for Linux and just about every portable device out there.

Assuming that you're not taking the Flex 2 route, read on...

Why don't we start by whetting your appetite? Take a look at Figures 1-4 for some ideas of the additional features available that you can't access directly from Flash Forms.

To use these controls as well as pretty much anything else Flex (version 1.5) has to offer you really need to know about just one thing: the PopUpManager. In Flex it's used to open a new window dynamically on top of the calling application or window.

If you're not already familiar with Flex, I'd suggest you start by looking at Adobe's Flex 1.5 documentation at livedocs.macromedia.com/flex/15/.

Here's the ColdFusion code needed to load the Flex code:

<cfform format="Flash" height="600" width="800" onLoad="initApplication()">
   <cfformitem type="script">
     function initApplication()
     {
       mx.managers.PopUpManager.createPopUp(this, Demo1, false);
     }
   </cfformitem>
</cfform>

As you can see, it's somewhat minimal: Simply a <CFFORM> tag that defines the height and width of my application, and an "onLoad" attribute that calls an embedded function (initApplication) that has a single line of code - the call to the PopUpManager. And that is all! ColdFusion simply provides a wrapper. Everything else is now done in Flex (MXML) code.

The createPopUp function has the following parameters:

My example opens a non-modal window as a child of the flash form. And the interesting part: the second parameter tells it to load an MXML file: Demo1.mxml. Yes, it loads an MXML file! This is what opens up the world of Flex to us since you can write pretty much anything in the native MXML and simply use it in this manner.

As you can see below, the MXML code for these demos is also very simple. (Note: The code below is simplified; see Listings 1-4 for the full code).

Demo1.mxml (Figure 1)
<mx:Canvas>
<mx:HSlider/>
<mx:HSlider/>
<mx:VSlider/>
</mx:Canvas>

Demo2.mxml (Figure 2)
<mx:Canvas>
<mx:TitleWindow>
<mx:FormItem>
<mx:NumericStepper/>
</mx:FormItem>
<mx:ControlBar>
<mx:Button/>
<mx:Button/>
</mx:ControlBar>
</mx:TitleWindow>
</mx:Canvas>

Demo3.mxml (Figure 3)
<mx:Canvas>
<mx:MediaPlayback/>
<mx:MediaDisplay/>
<mx:MediaController/>
</mx:Canvas>

Okay, so these demos are interesting, but if you're happy with the controls provided by ColdFusion Flash Forms then why bother? Well, if you've ever tried to write anything larger than a few forms, then chances are you've run into the dreaded 32k or 64k limit error. Build your applications this way and I doubt you'll see one of those again. Instead of cramming all the different forms and their associated code into a single form, thus rapidly reaching the size limitation, you can put each form into a separate MXML file, each of which has its own size limits. You can effectively build an application as large as you want.

I've built a large application for Sun using this technique. It loads faster than a normal Flash Form, presumably because it doesn't have to process the form tags to build the MXML. It consists of a main page with a tab navigator. Each tab has multiple form elements, and each opens a whole range of different pop-ups, many of which open further pop-ups. I made extensive use of the modal attribute of the createPopUp function since this means I don't have to worry about the user accessing anything but the topmost window.

If you have access to FlexBuilder then you have the added benefit of being able to build your forms visually, which is a welcome relief after fiddling for hours with CF tags to get the layouts just right.

A word of warning: take small steps! The compiler will warn you about some errors, but mostly you'll just get a blank screen if you have something wrong. Check the logs and you're unlikely to find anything more useful than a Java null pointer error.

You'll want to edit the flex-config.xml file on your development machine to give you as much debugging information as possible (typically located at C:\CFusionMX7\wwwroot\WEB-INF\cfform\flex-config.xml). Locate and change the values listed below, then restart the ColdFusion service.

show-all-warnings : true
show-binding-warnings : true

While you're editing this file, two other items of interest are:

You can set default attributes for each of the controls and containers available to Flex, as well as setup styles that can be applied to individual controls via the "styleName" attribute present on all of these. See Listing 5 for a sample CSS file.

Hopefully simply discovering that you can use the PopUpManager to load native Flex has set your minds to thinking about all the things you can now achieve. I'm going to show you one more example showing a simple form with a modal pop-up. I'll show you how to pass a data structure back and forth, and also show you a way around a well-known Flex bug.

We start with a simple form that shows three fields and an edit button (note: The CSS file listed in Listing 5 has been used to style this form). Figure 5 shows our example page, and Figure 6 shows a popup form to edit the details.

As you can see in Listing 6, the three text fields are bound to values in userObj, the data that's initialized in the function initData() that's called when the form loads. When you click the edit button, this code is run:

mx.managers.PopUpManager.createPopUp(this, UserEditForm, true,{parentref:this,userobject:userObj});

This opens our pop-up form (userEditForm.mxml - see Figure 6 and Listing 7) as a modal window. It also passes two pieces of data to our new window: parentref, which allows the pop-up to communicate back to our main form, and userobject, the object containing our user data. Also note the function setUserData() that our pop-up window will use to send the edited data back to the main form. This is defined as a public function, not private like our other (internal) functions.

As with the main form, the TextInput controls' values are bound to the data object, this time to userobject, which was passed from the parent form:

<mx:TextInput id="namefield" text="{userobject.name}" />

Looking at the function saveUser you'll notice that we validate before saving (Figure 6 shows that our form input values are not validated!) by making this call:

if (mx.validators.Validator.isStructureValid(this,'userdata'))


Notice though that rather than validate the input controls themselves I'm validating userdata. This is a data model that holds all the user data in one object, and it is this that's passed back to the parent window after saving.

<mx:Model id="userdata">
   <user>
     <name>{namefield.text}</name>
     <phone>{phonefield.text}</phone>
     <email>{emailfield.text}</email>
   </user>
</mx:Model>

This in turn is bound to the input controls, and as such is always "up-to-date." Validation is handled using the Flex validation controls:

<mx:StringValidator     field="userdata.user.name"/>
<mx:PhoneNumberValidator     field="userdata.user.phone"/>
<mx:EmailValidator     field="userdata.user.email"/>

This ensures that the user supplies required data and that the telephone number and e-mail address are correctly formatted. Assuming that the user supplies all the correct data then the save function eventually passes the data back to the parent window before closing itself. It does this by using the parentref that our main form passed when it opened the window and calls the public function that we created there to get the updated data.

parentref.setUserData(userdata.user);

The setUserData function in Demo5.mxml simply assigns the data objects passed to it to userObj, the object to which our Label fields are bound, so they immediately update with the newly saved values.

Now for a few "gotchas" that you'd might like to watch out for. The first is the pop-up window's close button. You'll notice in the code that I disable the entire TitleWindow control when I'm saving to prevent the user from making any further changes or pressing submit a second time. That works great, but the close button is always active. Function closePopup checks the window's enabled status prior to closing.

In this example the window closes just fine. However, there's a well-known Flex bug whereby, if you close a pop-up window with a drop shadow in the response handler of a remoting call then the window is removed but the drop shadow remains behind (see Figure 7).

One solution is simply to use styles to disable the default drop shadow. If, however, you'd like to use the drop shadow then these two lines of code will sort the problem out for you:

    windowObject.setStyle("dropShadow",false);
    windowObject.redraw(true);

The first simply removes the drop shadow. We do this before closing the window. The second is needed because of the way flash does its redraws. Without it you'll find that the window is closed before the drop shadow is removed. The code forces an immediate redraw. See Listing 8 for an example that uses remoting. Simply comment out those two lines to see the shadow bug in action. Listing 9 is a very simple CFC that handles the saveUser function.

Finally, few other things to note:

© 2008 SYS-CON Media