|
|
Flex 3 Tips, Tricks, and Gotchas (A Series) |
At Your Majesty, I just launched a small (2.5kloc outside libraries, 2 weeks dev/qa time) site using Flex 3: the Axe Vice Naughty to Nice sweepstakes site. I chose Flex for this project because of the exceedingly short dev cycle, and because it contained forms and validation I could take out-of-the-box. However, like any project, I ran into a fair share of problems from annoying to outrageously aggravating, and I feel like it always helps to document what I learned, not just for everyone else but for my future self. You’d be surprised how often I have to open up chapters I wrote myself for the AS3 Bible or Introduction to Flex 2 and refresh my “memory,” if you can even call my rattling collection of underused neurons such a thing. Read on for tons of tips, tricks, techniques, and tilapia.
General Impressions and Tactics
Flex is always a completely different mindset to think in, and even the amount of interactivity present in this limited site was too much to build 100% in Flex. I backed out and built the “game” (ha ha) portion of the site in Flash with AS3 and Tweener, even going so far as to use plenty of timeline animations. Let’s never forget that despite all our powerful coding techniques, hand animation has its place. This part is loaded in a SWFLoader and communicates using event bubbling (hooray!)
In this site I tried to synchronize the state of multiple different parts of the site using a centralized set of states. All of the elements on the site share a common set of states which are stored as static constants of a States class (call it something different yourself, since this interferes with autocompletion when you’re typing an <mx:states> property tag). I always use data binding to connect up to those states: <mx:State name=”{States.SEND_TO_FRIEND}”>. Navigations are sent right to the top (I ended up factoring out the navigator class since the navigations are so straightforward here), and then passed top-down, recursively when necessary. This simple approach to navigation let me break up the site into tiny chunks but still maintain a unified state without using different mechanisms for each part. The technique worked out well — for a tiny site like this.
I rediscovered that relying on states is great because state changes automatically have plenty of events and effects to bind to, and they are a great way to control transitions. I rediscovered that you should always resist the urge to bend a component to your will. Just because you have something that looks like a horizontal slider, you can spend endless hours trying to break into and override the operation of a Flex component, then realize that writing startDrag() isn’t really that hard.
I used resource bundles to easily do the frequent copy changes you know you can expect when a site has to pass legal review. This was a really good choice, and I love resource bundles, though they can be tiring to type and confusing to set up. I’ll cover them in more detail below.
I also rediscovered that flex effects are incredibly hard to do cool things with. However, let’s not forget that they’re super easy to do simple things with. I was groaning at the prospect of adding animated fade rollovers to all this text, then I went out and did it and it took like 5 seconds (much easier than writing all that code for event handlers):
<mx:Text ... rollOverEffect="{linkRollOver}" rollOutEffect="{linkRollOut}">
<mx:Fade id="linkRollOver" alphaFrom="0.15" alphaTo="0.3" duration="200"/>
<mx:Fade id="linkRollOut" alphaFrom="0.3" alphaTo="0.15" duration="350"/>
More general tips: Use Canvas for everything. Always set horizontalScrollPolicy and verticalScrollPolicy to “off”. Use clipContent=”false” to your advantage as well. Don’t forget the really powerful horizontalCenter=”0″ and verticalCenter=”0″ for aligning things, even inside non-layout-managed containers.
You can use Flash-style display list access. If you start feeling constrained by Flex but you don’t need to build a whole darn SWF for one thing, you can build the clips you like in the places they need to be, and use it as an asset inside an Image tag. Then you can use the content property to “break through” the Flex framework into the Flashy goodness inside. For example, take the red marker that fills up the clue bar as you find clues. Man, such a simple thing, but it’d be pretty annoying to do it right in Flex when it has to look just so! So I have all these clips laid out on stage and put in the right place, and I assign the top level clip a linkage class, export to SWF, and pull it into an Image:
<mx:Image id="clueBarClip" source="@Embed(source='/../assets/ui.swf',symbol='CluesIndicator')"/>
Then I can just tinker with the red bar inside it:
var fill:DisplayObject = DisplayObject(clueBarClip.content["fill"]); fill.scaleX = progress.numCluesFound / 3;
That was sooo easy. First I got my way into that library instance by accessing the content of the image, then see how I just grabbed the inner clip by its stage instance name “fill”? Use this creatively and don’t forget to mix in Flash when you don’t need Flexiness.
One last tip. Always check to see if someone else has built that thing you’re tearing your hair off to build. Flex components are so much easier to bundle up and reuse than flash components, I’ve found. Beside flexlib, there are several lists of useful components out there, like flexbox.
What do you mean, Flex doesn’t do that?
I kind of assumed that Flex did a few things for you that it does not. Let us not forget:
- Flex does NOT have a video player component! That’s right, all it has is a video display component. You’ll have to build your own scrubber, time display, controls, buffer screen, omg.
- Flex will NOT validate a whole form at once. Each one of the validators only works on its own. If you want to ensure a whole form is valid or not, tough titties. I thought there’d be an easy way to hack a bunch of validators together, but hours of hacking later, I was only frustrated. Thank goodness Paul Rangel came along and solved this problem rather handily. I used his form validator to control the enabled….ness? of my buttons.
- Flex does not seem to have a validator for combo boxes, radio groups, or check boxes. However, I built an exceedingly simple one which makes this a snap. See ComboValidator. This code uses it to validate a combo box (the first value is instructional and won’t be accepted) and a checkbox (which you need to check).
<validators:ComboValidator rejectedValues="{[0]}" source="{someComboInput}" property="selectedIndex" errorMessage="Select one." /> <validators:ComboValidator acceptedValues="{[true]}" source="{someCheckBox}" property="selected" errorMessage="check this box!"/>
Freaking Gotchas
Freaking Flex! Oh, how I cursed you.
- Preloaders. Flex, you utterly fail! You know nobody paying money for a site is going to allow the default Flex loader. And yes, you can specify your own preloader class with the preloader attribute on the Application, but that has serious issues with using embedded assets. Yes, there are brave people who have figured out how to do this, but without copying their solutions almost verbatim, including some mysterious parts (with the mime-type and casting to a ByteArray, using a Loader? Two classes?) I wasn’t able to get things to work. And even so, I’ve heard reports that while loading my swf, the AVM hangs until the assets gets loaded — sometimes popping the script timeout error. All in all, doing a custom preloader in Flex that uses assets is really essential, and is not very well supported or communicated about. When I use this technique, does the Flex compiler know to put all the assets in my preloader in the first frame? Who knows! I’d like a flex compiler option to specify a SWF or class reference as a preloader, and guarantee that all of that is compiled in frame 1. Yugh. Please fix for Flex 4!
- Scale 9. As I wrote about in Introduction to Flex 2, the big difference between skins and background images is that you can use scale 9 on your skins to let one image work in many different sizes without looking awkward. But for whatever reason, and nobody ever tells you this, you can’t use scale 9 with assets embedded from symbols in SWFs. It just doesn’t work. Regardless of whether you build the guides in Flash yourself, or write them in the embed tag. If you want to use scale 9 in your skins, pull them in as PNGs, and use the all-but-hidden embed “grid” attributes:
Button { up-skin: Embed(source="button/off.png", scaleGridLeft="10", scaleGridTop="7", scaleGridRight="51", scaleGridBottom="13"); over-skin: Embed(source="button/over.png", scaleGridLeft="10", scaleGridTop="7", scaleGridRight="51", scaleGridBottom="13"); down-skin: Embed(source="button/down.png", scaleGridLeft="10", scaleGridTop="7", scaleGridRight="51", scaleGridBottom="13"); disabled-skin: Embed(source="button/disabled.png", scaleGridLeft="10", scaleGridTop="7", scaleGridRight="51", scaleGridBottom="13"); }Don’t forget that you have to duplicate your skin for all states that might be used, or the Halo skin will show up instead (that kind of sucks). I wasted so much time trying to work around my scale 9 skins not scaling by 9. Sooo much time. But I made ‘em PNGs and suddenly everything is hunky-dory. AUGH.
- Disappearing fonts. Some Flex components make the brilliant move of forcibly applying a font style. So if you keep seeing your fonts disappear, check the type of the component. Buttons and all their subclasses will default to a font-weight of bold, so if you don’t embed the bold version of a font or remember to set font-weight to normal, you might not ever see your buttons’ labels, or they might only appear on your computer, or show up as Times New Roman or some default font on a user’s box. Always remember to test your sites while turning off the fonts that you used to build it!
Speaking of which, I have to endorse font face embedding using SWF fonts. It’s the only way to embed PostScript fonts (at least in Flex 2), you can set the antialiasing settings and the character ranges visually in the IDE, and most importantly, you can share files so easily without trying to get the same font working with multiple developers who might have different versions of the font, be running on different platforms that assign different names to the font, and any amount of other ridiculous fonttomfoolery. Just check in your font swfs, and everyone will be happy.
- Resource Bundles. These are awesome, but took me forever to get working properly. I think the documentation misses some info about how to set up your project for resource bundles. Anyway, if you’re building a site that might require frequent copy changes, or might appear in multiple languages, then you should definitely try out Flex resource bundles. In Flex 3, you can even swap out resource bundles at runtime. Yay!
So what’s a resource bundle? It’s just a collection of values that you stick in a file, and you can link into your program. Instead of writing the strings directly in your code, you can centralize them all for easy translation and changes. If you’ve built a lot of sites in Flash, I wager you’ve done this yourself with XML files at one point. Using the Flex version is great cause you can link directly to strings in MXML with some short syntax, rather than writing code. You can also use property files to store arrays, class references, and images that are localized. Awesome! F.ex.
string1=Hello string2=World
<mx:Label text="@Resource(key='string1',bundle='Strings')"/> <mx:Label text="@Resource(key='string2',bundle='Strings')"/>
But I ran into problems getting things working. First, the stuff that made sense:
- You can link to resources either by using the @Resource(key=’KeyName’, bundle=’BundleName’) directive in MXML, or using the [ResourceBundle("BundleName")] metadata tag to create a reference to a ResourceBundle in ActionScript.
- You can either specify the bundle name in these methods to specify which property file to reference, or without it, the property file with the same location as the containing class will be used. For example, if you omit the bundle name in com.example.Rectangle, Flex will look for the key in the com/example/Rectangle.properties file. I used a single bundle and always included the bundle name.
- When you use ActionScript to reference a ResourceBundle object, you can use methods on it to retrieve values as other things than strings. I use this to fill in combo boxes:
signupReferrerOptions=Select an option, MySpace Page, Web banners, TV, Friend
<mx:ComboBox id="age" selectedIndex="0" dataProvider="{Main.stringBundle.getStringArray('signupAgeOptions')}"/>
But what wasn’t clear to me was how to set them up.
- Typically, you create a path for resources, such as /locale/, in your project root. Then each language gets its own subdirectory under here, with its own copy of all the property files, such as /locale/en_US/Strings.properties, or /locale/en_US/com/example/Rectangle.properties
- Whatever directory is the root of the particular language’s property files (/locale/en_US/) needs to be in the source path when you build the project. Flex Builder doesn’t set this up automatically, even if you add the locale/ dir to the source path and set -locale en_US in the compiler arguments.
- To set things up properly in Flex Builder, make sure the current locale is set in the Flex Compiler options with the -locale option. Then, add the folder locale/{locale} (literally) to the source path. Flex Builder or mxmlc should substitute the correct locale at build time. Only when the property files are in your build path will the bundles be reachable. The compiler doesn’t provide very helpful errors when you do this wrong.
- Multiline controls. Why Flex doesn’t allow you to flow the labels for buttons, checkboxes, radio buttons, and the like to more than one line is just aggravating. I hacked these components to allow it, but I didn’t have time to get the sizing correct, instead hacking in the right amount of padding manually… oh well.
- Where is the root for embeds anyway? Looks like it’s in the root of the source path. So if you have a folder structure with /src/com/example/Application.mxml trying to reach /assets/whatever.gif, you can actually always get to the assets with this odd path: /../assets/whatever.gif.
Specific Techniques
So, like most of my projects, I tried out some fun stuff in this project. This project required some PHP scripting as well. Sending variables in a POST/GET to PHP scripts is easy in AS3, but you know, just not quite easy enough to warrant typing it over and over, so I made a little helper class, FormSubmitter, with the method
public function send(url:String, hash:Object, method:String = URLRequestMethod.POST):IAsynchronousToken
Then I extend it for some services just to make the URLs to the scripts constants:
package com.axevice
{
import com.yourmajesty.services.XMLFormSubmitter;
public class Service extends XMLFormSubmitter
{
public static const SEND_TO_FRIEND:String = "somesendtofriendscript.php";
public static const SIGNUP:String = "somesignupscript.php";
}
}
And then calling them and reacting to them is so nice.
var hash:Object = {yourName: yourName.text, yourEmail: yourEmail.text, theirName: theirName.text, theirEmail: theirEmail.text};
(new Service()).send(Service.SEND_TO_FRIEND, hash).addEventListener(AsynchronousTokenEvent.COMPLETE, onSendComplete);
That’s all for now! In the next installment, I want to cover a class I wrote that uses introspection to automatically bind models to XML.
September 14th, 2007 at 11:13 am
Hi Roger,
Great commentary. I’m hoping that you’ve filed lots of bug reports and enhancement requests at http://bugs.adobe.com/flex or made comments on the livedocs version of the documentation where appropriate. There’s definitely elements mentioned here that we already knew we needed to improve, but having details makes it all the easier to address.
September 14th, 2007 at 7:32 pm
[...] sweeps site. Though it’s nothing revolutionary, you can head over to my blog and read about some of the techniques that went into its development. At Your Service. Comments [...]
September 15th, 2007 at 2:27 pm
oh man… that Axe Vice Naughty to Nice is hilarious!
September 18th, 2007 at 8:14 pm
Nice work… I have only dabbled with Flex so far, as a web developer it requires a different mind set with application flow since ( even with Web 2.0 ) you are stuck in the request / response world. Being able to bring large amounts of data client side and have this modified and sync up later requires rewiring heavy old cables inside my brain.
September 27th, 2007 at 8:21 am
So the book “Flex 2/3 for the Real World” is due out when? :) Three months into Flex 2 and I have learned SO MUCH. I was one who missed the Java boat and wandered aimlessly for a couple years in CRM (very old VB language) then moved to the taboo side of Flash and table based web sites. Now aboard the Flex boat I have a very bright spot light guiding me in the path of a better future… Long live Flex, AS3, and the leaders who are helping the new crew catch up.
…ramblings of a drowned rat…
September 27th, 2007 at 5:28 pm
@GregM, well there’s my book Everything You Always Wanted to Know About Flex :)
October 30th, 2007 at 8:56 am
Roger, with regards to the resource bundle, you may want to let the user know that the source path of your resource must be relative to the main application source path. It took me a while to realize that it was checking against /src in my AIR project.
July 28th, 2008 at 3:26 am
you can?t use scale 9 with assets embedded from symbols in SWFs.
That just work fine in flex 3 now. I don’t know for 2 btw?
October 15th, 2008 at 1:20 pm
Great stuff! Flex is a powerful tool that needs some serious polishing. There are so many of these “gotcha’s” that unless you’re serious about Flex, you could throw your hands up in frustration.