Tuesday, July 21, 2009

Drag and Drop: double binding?

We had a minor fracas at work this morning. The burning issue: does Flex 3, in fact, have double binding?

The official answer is no, Flex 3 does not have double binding. But... it seems that in certain cases, doubly-bound behavior is nevertheless present.

For example:

* create a tree, and enable drag and drop behavior
* set your tree's data provider as an ArrayCollection with nested Arrays; in other words, a treelike structure.
* grab a handy data serialization package, such as as3yaml (http://code.google.com/p/as3yaml/), to display the data structure, then run in debug mode.

Then start dragging elements around, and you'll see something natural: as elements change position, the underlying data structure changes. In other words, the data provider is the model, and the Tree is the view... but also the controller. Change the tree, change the underlying data provider.

Acts like double binding. In reality, it's boilerplate code underwiring the drag and drop attributes on things like Tree and DataGrid. Remember: [Bindable] is boilerplate code forcing the view to change when the model changes. And this one sort of seems like the reverse... hence it is very like double binding.


My sample code:

<?xml version="1.0" encoding="utf-8"?>
<mx:Application
xmlns:mx="http://www.adobe.com/2006/mxml"
layout="absolute"
creationComplete="handleCreationComplete()">

<mx:Script>
<![CDATA[
import mx.collections.ArrayCollection;
import org.as3yaml.*;

private var node1:Object =
{
label: 'parent 1',
index: 0,
children:
[
{ label: 'A' }, // , index: 0 },
{ label: 'B' }, // index: 1 },
{ label: 'C' }, // index: 2 }
]
};

private var node2:Object =
{
label: 'parent 2',
index: 1,
children:
[
{ label: 'D' }, //index: 0 },
{ label: 'E' }, //index: 1 },
{ label: 'F' }, //index: 2 }
]
};

private var node3:Object =
{
label: 'parent 3',
index: 1,
children:
[
{ label: 'G' }, //index: 0 },
{ label: 'H' }, //index: 1 },
{ label: 'I' }, //index: 2 }
]
};


[Bindable]
private var mydata:ArrayCollection = new ArrayCollection([node1, node2, node3]);

private function handleCreationComplete():void
{
dump();
}

private function dump():void
{
var s:String = YAML.encode( mydata.source );
trace( s );
}
]]>
</mx:Script>
<mx:Tree id="mytree"
dataProvider="{mydata}"
dragEnabled="true"
dropEnabled="true"
dragComplete="dump()"
editable="true"
x="40" y="40" width="300" height="300"
/>
</mx:Application>

Monday, July 20, 2009

Refresh that ArrayCollection

So I have this ArrayCollection, which looks basically like this:

[Bindable]
public var d1:ArrayCollection = new ArrayCollection(
[
{ label: 'first', code: 'A', num: 4 },
{ label: 'sec', code: 'B', num: 7 },
{ label: 'tria', code: 'C', num: 9 }
]
);

It's the dataProvider for a DataGrid, like this:

<mx:DataGrid id="dg1"
dataProvider="{d1}"
dragEnabled="true"
dragDrop="handleDragDrop(event)"
dropEnabled="true"
x="320" y="126">
<mx:columns>
<mx:DataGridColumn headerText="Column 1" dataField="label"/>
<mx:DataGridColumn headerText="Column 2" dataField="code"/>
<mx:DataGridColumn headerText="Column 3" dataField="num"/>
</mx:columns>
</mx:DataGrid>

And then, I've got a horizontal slider that sets a value in the ArrayCollection, like this:

<mx:HSlider id="hslider" x="10" y="320" width="302"
minimum="1" maximum="10" snapInterval="1"
change="d1.getItemAt(0).num = this.hslider.value"
labels="{[1,2,3,4,5,6,7,8,9,10]}" />

So I can slide the slider, and the "num" attribute of the first element in d1 changes, but the display list in the datagrid doesn't change. And so I learned (or perhaps re-learned) a lesson today:

Whenever you change data in a data provider, also call refresh() on it.

Not only does this re-run filters and sorts, it also refreshes the display. So when a single value changes, calling refresh() will update display lists that are bound to the array.

So, the slider's change property should look like this:

change="d1.getItemAt(0).num = this.hslider.value; d1.refresh()"

I'm actually using these widgets in a web application I'm slowly working on, which has a web of components that interact with each other. It's wargame rules for starship combat for a pencil-and-paper game called Traveller:

http://eaglestone.pocketempires.com/rules/t5/starships/Shipyard.html

Monday, July 6, 2009

Data Binding is Beautiful

One of THE best things about Flex is its data binding. Back in Java days, you had to write synchronization code for every GUI component backed by a data model. Thanks to Flex, all that boilerplate boredom is GONE. As a result, I can populate data grids with ease. For example, check out this Flex app I built in a few hours yesterday:

http://eaglestone.pocketempires.com/rules/t5/starships/TechChooser.html

Monday, May 11, 2009

Comprehensive Ruby Archive Network

As I was posting on my Flex blog, I started to wonder if there's a design assumption going on that I hadn't considered.

I was thinking that Ruby could use a repository where individuals can share modules. Basically, a large collection of software and documentation.

Or do we?

Take Perl. Do you want to turn your Perl script into a CGI system? Go to CPAN -- the Comprehensive Perl Archive Network -- and grab the CGI module (okay, bad example, it's included in the standard distribution, but you get the idea). CPAN has bazillions of modules, and is an invaluable resource for handling whatever you need to handle. Proofs of concepts get hammered out, new ideas get born, and thoughtful robust code is freely available. All in one practical place.

But, perhaps Ruby has a higher level of focus. I see RubyForge: support for "projects", i.e. full applications, hinting that the modules there require just a bit too much cohesion to be generally useful. Instead of plug-in modules that you can use to add functionality to an application, does Ruby tend to focus on the applications (or projects) themselves?

Comprehensive Flex Archive Network

We need a repository where individuals can share ActionScript and Flex modules. Basically, a large collection of Flex software and documentation.

Or do we?

Take Perl. Do you want to turn your Perl script into a CGI system? Go to CPAN -- the Comprehensive Perl Archive Network -- and grab the CGI module (okay, bad example, it's included in the standard distribution, but you get the idea). CPAN has bazillions of modules, and is an invaluable resource for handling whatever you need to handle. Proofs of concepts get hammered out, new ideas get born, and thoughtful robust code is freely available. All in one practical place.

But, perhaps Flex has a higher level of focus. I've noticed that most of the modules we create require just a bit too much cohesion to be generally useful. Instead of plug-in modules that you can use to add functionality to an application, Flex tends to focus on the applications (or projects) themselves. So it's a higher-level view.

Thursday, May 7, 2009

Password Strength Meter

http://eaglestone.pocketempires.com/flex/passwordMeter/PasswordMeter.html

I found a good starting example of a password strength meter at http://subeesh.wordpress.com/2008/02/19/flex-password-strength-meter/#comment-69

It's got a nice graphical component, and great regular expression checkers, but the algorithms used to evaluate password strength are unknown to me. I imported his gradient bar into my component, which is simply a VBox which contains the graphics context for the gradient and algorithm. The component also requires a "dataProvider", which is just the TextInput component you wish to evaluate.

The source is here:

http://eaglestone.pocketempires.com/flex/passwordMeter/Password.mxml

Monday, May 4, 2009

ComboBox Mediator

Friday I applied the Mediator pattern to a pair of ComboBoxes.

The Mediator, in short, is an interaction between two objects that's been encapsulated into a third object. A go-between. It's almost like a Decorator that works on two objects instead of one.

We have Combo Boxes to choose your country and state. So when a user picks a country, then the list of states has to be filtered for valid selections. We do this all throughout our code for various custom forms (the forms themselves all vary in significant ways, otherwise we could simply re-use one form).

Instead of cloning our code over into yet another class, I wrote a ComboBoxMediator, which wires two ComboBoxes together in a source-sink relationship. The MXML looks like this:


<bgs:ComboBoxMediator
sourceBox="{countryCodeComboBox}"
sourceSelectedValue="{address.countryCode}"
targetBox="{provinceCodeComboBox}"
targetSelectedValue="{address.provinceCode}"
/>


The class itself is short. It sets up filtering in the province ComboBox and adds a change event listener on the country ComboBox.

One important step that I originally didn't anticipate is when the province box is being refreshed: I have to check to see if the filtered data results in no data at all (in other words, in cases where a chosen country doesn't have province data yet assigned to it). In this case, I set the data provider on the province box to an inline array with only one value: "Not Applicable".