Making Flex's PopUpMenuButton More Helpful

August 25, 2009 at 06:09 PM | ActionScript | View Comments

If you're anything like me, you cringe every time you think about implementing a popup menu in Flex: the options, PopUpButton and PopUpMenuButton, are both terrible.

For example, PopUpButton makes you jump through the hoops of creating a Menu and PopUpMenuButton forces you to create an event handler just to get the currently selected item.

Well, cringe no more! I've done a bit of hacking on the PopUpMenuButton to make the most common use-case, "showing a list of options", a little bit less painful by adding a 'selectedItem' field:

    <mx:XML id="colors">
        <colors>
            <color name="Red" rgb="0xFF0000" />
            <color name="Green" rgb="0x00FF00" />
            <color name="Blue" rgb="0x0000FF" />
        </colors>
    </mx:XML>
    
    <HelpfulPopupMenu id="colorMenu"
        dataProvider="{colors}"
        labelField="@name"
        showRoot="false" />

    <mx:Label
        color="{XML(colorMenu.selectedItem).@rgb}"
        text="You've chosen: {XML(colorMenu.selectedItem).@name}" />

Which can also be used to set a temporary default option:

    <mx:Script><![CDATA[
        var people:Array = [
            { name: "Bob", age: 16 },
            { name: "Alice", age: 42 },
            { name: "Jo", age: 31 }
        ];
        
    ]]></mx:Script>
    
    <HelpfulPopupMenu id="peopleMenu"
        dataProvider="{people}"
        labelField="name"
        selectedItem="{ { name: '(select a person)', age: null } }"
        showRoot="false" />

    <mx:Label
        visible="{ peopleMenu.selectedItem.age !== null }"
        text="That person is { peopleMenu.selectedItem.age } years old." />

Useful? I'd like to hope so.

You can see a demo by clicking the image below (sorry, I don't actually know the first thing about embedding SWFs into HTML, and I don't feel up to learning right now):

And you can download the source (which is MIT licensed) from more or less the same place: http://wolever.net/~wolever/HelpfulPopupMenu/.

Also, this is a small part of a larger library of helpful ActionScript utilities which I'm going to be releasing… At some point.

Permalink + Comments

Using Hamcrest to Filter ArrayCollections

August 11, 2009 at 12:03 PM | ActionScript | View Comments

Neil Webb's post on filtering an ArrayCollection on multiple property/values reminded me of how I recently solved exactly the same problem using Hamcrest-AS3.

So, you know the story: you've got an ArrayCollection that's full of, say, Laptops, and you want to filter it by, say, price, size and (of course) color.

Because you're a good programmer you always try to do the simplest thing that could possibly work first, and end up with a function something like this:

function laptopFilter(laptop:Laptop):Boolean {
    if (laptop.price > appliedFilters.maxPrice)
        return false;

    if (laptop.size > appliedFilters.maxSize)
        return false;

    if (laptop.color != appliedFilters.preferredColor)
        return false;

    return true;
}

Cool - that defiantly works, and it's pretty simple.

"But wait a second", you're thinking, "what happens when the rules get more complex? That function is quickly going to loose its simplicity!"

Enter, Hamcrest.

Hamcrest is a library of "matchers" which are often used to build unit tests. Here is a quick example of a unit test written with Hamcrest:

[Test]
function testApplePicker():void {
    var apples:Array = ApplePicker.pick("granny smith");
    assertThat(apples.length, greaterThan(4));
    assertThat(apples, everyItem(hasPropery("color", "green")));
}

But, of course, there is no reason that these same matchers shouldn't be used for other things too.

Other things like, say, filtering an ArrayCollection:

var filters:Array = [
    hasProperty("price", greaterThan(1000)),
    hasProperty("color", either("red").or("blue").or("green")),
    hasProperty("size", between(10, 15))
];

var laptopFilter(laptop:Laptop):Boolean {
    return allOf.apply(null, filters).match(laptop);
}

Of course, I've hard-coded the values in this example, but it doesn't take much creativity to imagine how they could be dynamically generated, keeping the code nice and simple while still allowing for complex rules.

So, that's how I filter ArrayCollections :-)

Permalink + Comments