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