July 31, 2006

Runtime Skinning In Flex 2

Check out Brian Riggs new post on runtime skinning in Flex 2.

Posted by mchotin at 11:53 AM | Comments (0) | TrackBack

August 08, 2005

Flex and Flash Player 8

Two articles posted that talk about Flex 1.5 and Flash Player 8.

Check 'em out!

Flash Player 8 and Flex 1.5
Using Flash Player 8 Features in Your Flex 1.5 Application

Posted by mchotin at 09:06 AM | Comments (0) | TrackBack

May 18, 2005

Embedding HTML in a Flex App?

Check out Christophe's article on embedding HTML in a Flex app by using an IFRAME. There are plenty of times when this approach will be inappropriate, but when it's right it's pretty sweet.

Posted by mchotin at 10:47 PM | Comments (3) | TrackBack

January 21, 2005

Localizing Validator Errors

A common request I've seen is a way to localize the Validator error messages. Unfortunately the built-in Validator classes don't make that easy in their current state (something we're hoping to work on in the next version). In the meantime, here is one approach (and I'm sure there are more if you wanted) to changing the error messages using some form of resource file.

First we need a resouce file, I'm gonna format mine to be as close to the validator names as possible:

validatorStrings.xml
<validators>
<StringValidator>
<tooLongError>Localized too long</tooLongError>
<tooShortError>Localized too short</tooShortError>
</StringValidator>
</validators>

Note that the validators is just an outer tag to make this valid XML. Inside I would put one tag for every validator. Here I just did StringValidator, but you can imagine a NumberValidator tag sitting right underneath it. Inside the StringValidator tag I put an entry for each error message, the name of the tag is the error name and the body of the tag is the actual value.

Now we need this resource loaded, so I'm just gonna use an HTTPService to do that, then I'm going to store the result in a Model that will be accessible from anywhere.

app.mxml
<mx:Application xmlns:mx="http://www.macromedia.com/2003/mxml" xmlns="*" initialize="validatorStringsSvc.send()">

  <mx:HTTPService id="validatorStringsSvc" url="validatorStrings.xml" />
<mx:Model id="validatorStrings">
{validatorStringsSvc.result.validators}
</mx:Model>

Now we need a Validator to actually use this resource.

StringValidator.as
class StringValidator extends mx.validators.StringValidator
{
    private var stringsCopied : Boolean = false;

    private function copyStrings() : Void
    {
        //if for some reason the HTTPService is slow to load this may need to re-execute, hence the extra checks and repeated calls
        if (!stringsCopied)
        {
            var strings : Object = mx.core.Application.application.validatorStrings.StringValidator;
            if (strings != null)
            {
                for (var i in strings)
                {
                    this[i] = strings[i];
                }
                stringsCopied = true;
            }
        }
    }

    public function doValidation(value) : Void
    {
        copyStrings();
        super.doValidation(value);
    }

}

You could basically copy this code and do it for any validator that already exists. I'm sure there is a better way to optimize the copyStrings approach to avoid the repetitive calls (for example you could put the if check in the doValidation method so you pay the price of a compare instead of a function call and THEN a compare), but I think it's not a big deal.

And now finally, how do you use this lovely new Validator? Just use the normal validator tag and change the namespace to point to your local version.

TADA!

The story of why Validators are architected in such a way that this is difficult is a story for another time :-)

Posted by mchotin at 04:31 PM | Comments (3)

November 18, 2004

Guest Post on File Upload

Jimmy Gianninas of Optimal Payments has been kind enough to write a quick example of one way to get file upload into your Flex app. Rather than write a full-blown devnet article which will take weeks to get edited and posted, we're putting it up on my blog to get it out faster. You can get the examples here. There is a Word document describing some of the files, and then a number of files that you can use as templates to get yourself going. Note that none of it will run out of the box, you must edit files and set up your server as appropriate.

Thanks again to Jimmy for the community contribution. If you have questions you might be better off posting on flexcoders rather than in the comments since I'm not sure how often he'll check them.

Posted by mchotin at 06:09 PM | Comments (4)

October 13, 2004

foo(Void) vs foo()

Sometimes the benefits of a scripting language can come back to bite you in the rump :-) One thing that I've seen a lot of recently is the practice of declaring a function to have a single parameter: Void. The intention here is to indicate that the function takes NO parameters, a feature that other languages sometimes support. However, in ECMAScript (and thus ActionScript) it does NOT have that meaning. In fact, the compiler instead declares a local variable called Void! You can debate all you want about how wrong or right this is, but it's the way the language is designed.

Despite some of the additions to the language in AS2, ActionScript is still a scripting language and still allows you to pass a different number of parameters than what is declared in the function signature. In Flex we added some compiler checking to make sure that if you declare a variable or function that is already declared in the superclass, the type or signature matches between the two. This is to help prevent developers from accidentally overwriting a variable they didn't know existed in the superclass. However it also catches something like our (Void) problem. All shipped Flex code does not use the (Void) convention. If you then override a function and use that convention (which may have been done a lot in in the V2 components with MX 2004) you will see the compiler give you an error about Void not matching the superclass type.

Not sure how clear this is but I hope it can be of some help.

Posted by mchotin at 05:52 PM | Comments (2)

October 04, 2004

Using a proxy to detect changes to models

Someone recently asked how you might intercept changes on an object so that you could know when it is dirty and needs saving. The catch was that he wants to use binding to populate the object and doesn't want to put event handlers on his controls just for this monitoring. Finally, he wants the solution to be generic (meaning that writing your object to have setters and monitor its own dirty state is not acceptable).

I've written up one solution to the problem here, using a proxy object to pass changes through to the final object, and using the proxy as the indicator of when things are dirty.

import mx.events.EventDispatcher;

/**
 * Dispatched whenever the proxy passes on a change to the destination.  Event properties are:
 * type: 'change'
 * target: this proxy
 * property: the name of the property changing
 * oldvalue: the value before the property is changed
 * newvalue: the new value of the property
 * wasPreviouslyDirty: true if the isDirty flag was set BEFORE this property was set
 */
[Event("change")]

/**
 * This class can be used to proxy changes through to another object.  It is given a list of properties to proxy
 * and will pass on any changes that are different than the value already set.  In addition it will dispatch
 * a 'change' event indicating that the property has changed.  Finally it will mark itself dirty so that
 * it can be queried as to whether anything has changed.
 */
dynamic class ChangeDetectingProxy
{
    //these functions will be filled in during event dispatcher initialization
    //they need to sit above the real fill-in so that it gets overridden
    function dispatchEvent(obj : Object) : Boolean { return true; }
    function addEventListener(event : String, handler) : Void { }
    function removeEventListener(event : String, handler) : Void { }

    //this makes us an event dispatcher
    static function _SetupEventDispatcher_() : Object
    {
        var o:Object = ChangeDetectingProxy.prototype;
        EventDispatcher.initialize(o);
        return undefined;
    }

    private static var _sed_ = _SetupEventDispatcher_();
    private static var _dependsOnEventDispatcher_ : EventDispatcher = EventDispatcher;

    private var __propertyList : Array;

    /**
     * The object to which changes will be passed.
     */
    public var proxyFor : Object = null;

    /**
     * Indicates whether this object has made any changes.  It is useful if you want to monitor the
     * saved state of the 'proxyFor' object.  You may reset isDirty any time (though it will be set
     * to true immediately after dispatching a change event).
     */
    public var isDirty : Boolean = false;

    [ChangeEvent("propertyListChanged")]
    /**
     * An array of strings representing the properties that this proxy will pass through.
     */
    public function get propertyList() : Array
    {
        return __propertyList;
    }

    public function set propertyList(pl : Array) : Void
    {
        __propertyList = pl;
        //for every property create a getter to return the proxied object's value and a setter
        //which will pass the change through (only if newvalue != oldvalue) and dispatch the
        //change event
        for (var i = 0; i < pl.length; ++i)
        {
            var propname = pl[i];
            //arguments.callee refers to the function itself and is a way to make sure that each function has its unique
            //instance of the property name
            var getter = function()
            {
                return this.proxyFor[arguments.callee.propname];
            }
            var setter = function(newvalue)
            {
                var pn = arguments.callee.propname;
                var oldvalue = this.proxyFor[pn];
                if (oldvalue == newvalue) return;
                this.proxyFor[pn] = newvalue;
                this.dispatchEvent({type: "change", property: pn, oldvalue: oldvalue, newvalue: newvalue,
                                    wasPreviouslyDirty: this.isDirty});
                this.isDirty = true;
            }
            getter.propname = propname;
            setter.propname = propname;
            var tmp = undefined;
            if (this[propname] != undefined)
            {
                tmp = this[propname];
                delete this[propname];
            }
            this.addProperty(propname, getter, setter);
            if (tmp != undefined)
            {
                proxyFor[propname] = tmp;
            }
        }
        dispatchEvent({type: "propertyListChanged"});
    }

}

Using this class you can now create MXML like this:

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.macromedia.com/2003/mxml" xmlns="*" backgroundColor="#ffffff" marginTop="0">

    <mx:Script>
        function save()
        {
            //somehow save the model
            cdp.isDirty = false;
        }

        function exit()
        {
            if (cdp.isDirty)
            {
                alert("You haven't saved your data, please do so before leaving.", 'Save Needed');
            }
            else
            {
                alert('Goodbye');
            }
        }
    </mx:Script>

    <mx:Model id="mod"/>

    <ChangeDetectingProxy id="cdp" proxyFor="{mod}">
        <propertyList>
            <mx:Array>
                <mx:String>name</mx:String>
                <mx:String>street</mx:String>
            </mx:Array>
        </propertyList>
    </ChangeDetectingProxy>

    <mx:Binding source="nameInput.text" destination="cdp.name"/>
    <mx:Binding source="streetInput.text" destination="cdp.street"/>

    <mx:Panel title="ChangeDetectingProxy Demo" id="p">
        <mx:Form>
            <mx:FormHeading label="Change an input and press exit to see how it works"/>
            <mx:FormItem label="Name">
                <mx:TextInput id="nameInput"/>
            </mx:FormItem>
            <mx:FormItem label="Street">
                <mx:TextInput id="streetInput"/>
            </mx:FormItem>
        </mx:Form>

        <mx:HBox>
            <mx:Button label="Exit" click="exit()"/>
            <mx:Button label="Save" click="save()"/>
        </mx:HBox>
    </mx:Panel>

</mx:Application>

Which in turn will produce a Flex app like this:

Posted by mchotin at 04:08 PM | Comments (5)

June 22, 2004

Test from a distance

I've joined a project recently where I've been at the Macromedia office in San Francisco but the server I've been hitting is across the country. As soon as I started working I saw real performance problems where the application wouldn't start for a long time. It turned out that there was a lot of data required for the app to initialize, but now that the data had to travel outside of the intranet its size became noticeable. This required a real adjustment for the application. The engineer decided to pass down thin versions of each object and as necessary load the additional details instead of getting all of the data at once . This reduced the amount of data on the wire significantly and allowed the app to start up in a reasonable time. Getting more data on demand produces a slight delay, but you can "entertain" the user easily enough to pass the time.

Another thing that we saw is that a RemoteObject call that had been working for the developers across the country was not working for me. It turned out that we had concurrency set to single for a method on one of our RemoteObjects. For the developers working locally the first call to this method was returning in time for the second call to succeed. But when working from a distance the second call would fault because the first call had not completed. Add in the fact that the fault handler for this particular RemoteObject accidentally was swallowing faults and not reporting them, and we had a pretty frustrating debugging experience.

The point of this little anecdote is that it took too long for the engineers to know that they had some architecture problems because all of their testing was using a local database and simply testing against localhost. So please, as you're developing, make sure to occasionally test your app from a browser that's not on the same machine as your server. And if possible have someone outside of your intranet connect to your Flex server and see how the performance goes. If you want to get a real sense of what's going back and forth you can use a packet sniffer to watch the network traffic (I like ethereal). You'll be able to see how many images are requested (also a big deal when distance is involved), the size of your data, and even what some of that data looks like on the wire.

Testing this kind of stuff early yourself can save you from some real pain later on. You don't want QA to start complaining the first time they try your app (or even worse, your VP looking from home).

Posted by mchotin at 11:43 PM | Comments (1)

May 27, 2004

Debugging Databinding

The Flex databinding system is a relatively simple yet powerful mechansim for moving data throughout your application. However, there are times when binding will appear not work, and I'd like to give some suggestions on tracking that down.

  1. Don't ignore warnings! It's easy to see a warning and think that it doesn't matter, especially if binding appears to work at startup. Warnings actually mean something! If the warning is about a missing [ChangeEvent] on a getter/setter it means that even if the binding works at startup, subsequent changes to the property will not be noticed. If the warning is about a static variable or built-in property, changes will not be noticed. Finally, if you're sure that the property should be bindable but the compiler is complaining about an unknown type, you may need to cast an object in your binding expression so the compiler can find the appropriate type information (including [ChangeEvent] metadata). The Flex docs should discuss this in more detail.
  2. Make sure that the source of the binding did actually change. This seems obvious, but it's easy for your source to be part of a larger procedure and to miss the fact that it was never assigned.
  3. Make sure that the ChangeEvent is being dispatched. You can use fdb or another debugger to make sure that your dispatchEvent call is happening. Also, you can add a normal event listener to that class and make sure it gets called (if you want to add the event listener as a tag attribute you'll need the [Event('myEvent')] metadata at the the top of your class definition or in the <mx:Metadata/> section of your MXML).
  4. Create a setter function and use a <mx:Binding/> tag to assign into it. You can then put a trace or an alert or some other debugging code in the setter with the value that is being assigned. This technique makes sure that the binding itself is working. If the setter is called with the right information, you now know that it's your destination that is failing and you can start debugging in there.

If you've tried the above and things still aren't working it might be time to get some code on flexcoders or the support forums (support forums are obviously more official but as of this writing flexcoders is pretty responsive). If at all possible bring your problem down to one or two files (this may mean creating a separate simulation just for discussion) so that you don't need to share your whole source and we can work with just the problem. If you can't send a simple subset the next thing is at least the files that are causing problems along with the -generated.as debugging file (which you can get by setting <keep-generated-as>true</keep-generated-as> in flex-config.xml.

If you have any questions on the binding system that aren't discussed in the docs feel free to ask them here.

Posted by mchotin at 07:27 PM | Comments (0)