Bind dataprovider DataGrid to an Array - apache-flex

How do you bind the dataprovider of a DataGrid in Flex to an Array?
This doesn't seem to work:
<mx:DataGrid
id="valuesDataGrid"
editable="true"
width="100%"
height="100%"
dataProvider="{_metaDataKey.values}">
...
[Bindable]
public class EnumMetaDataKey{
private var _values:Array = [];
public function get values():Array { return _values; }
public function set values(value:Array):void { _values = value; }
...

Use an ArrayCollection instead. Arrays do not lend well to binding. IIRC, this is documented in the Flex 3 help on Binding to functions Objects and Arrays:
Note: When defining a data binding expression that uses an array as the source of a data binding expression, the array should be of type ArrayCollection because the ArrayCollection class dispatches an event when the array or the array elements change to trigger data binding. For example, a call to ArrayCollection.addItem(), ArrayCollection.addItemAt(), ArrayCollection.removeItem(), and ArrayCollection.removeItemAt() all trigger data binding.

Related

Flex ItemRenderers

I know an ItemRenderer is a ClassFactory and that you can use the newInstance method of ClassFactory to get an instance of the ItemRenderer. My question, however, is is it possible to use methods of the ItemRenderer without using ClassFactory.newInstance()?
In my case I can't use this newInstance method because it doesn't keep the state.
Is there any way I can do this? Thanks!
An ItemRenderer is a component, like any other. The itemRenderer property of a list based class has a value of a ClassFactory. If you have a reference to an instance of the itemRenderer component, you can call methods on it.
You cannot call a method on any component if an instance if that component instance has not been created yet. So to call a method on an itemRenderer without using ClassFactory.newInstance() you must manually create your own instance using the new keyword.
You might want to implement the ItemRenderer as smart as it is needed to recreate the state depending in the data being set. On the other hand, make sure that the data contains everything needed. You barely want to interact with the renderers in a different scope then the renderer itself.
If it should necessary, a DataGroup dispatches a RendererExistence event when a renderer is added.
private function newList():List {
const list:List = new List();
list.addEventListener(FlexEvent.INITIALIZE, list_initializeHandler);
return list;
}
private function list_initializeHandler(event:FlexEvent):void {
const listBase:ListBase = ListBase(event.target),
dataGroup:DataGroup = listBase.dataGroup;
dataGroup.addEventListener(RendererExistenceEvent.RENDERER_ADD, dataGroup_rendererAddHandler);
dataGroup.addEventListener(RendererExistenceEvent.RENDERER_REMOVE, dataGroup_rendererRemoveHandler);
}
private function dataGroup_rendererAddHandler(event:RendererExistenceEvent):void {
// renderer added
}
private function dataGroup_rendererRemoveHandler(event:RendererExistenceEvent):void {
// renderer removed
}
This is the way to go if you need to reference single item renderer instances.
Do you mean static functions and variables?
If you define a function (or variable, or const) as static, it is accessible via the class name, so you could define
class MyClass {
public static const className:String="MyClass.className (const)";
public static function getClassName():String {
return "MyClass.getClassName (function)";
}
}
trace(MyClass.className); //prints "MyClass.className (const)"
trace(MyClass.getClassName()); //prints MyClass.getClassName (function)

How do I append an item to my dataProvider? (Flex)

What I would like to do is simply add to a dataProvider, but when I do, I get an error.
Here's the code I'm trying to run...
dg.dataProvider.addItem(obj.ResultSet.Result[i]);
It's inside a for loop, using i as the integer.
It works great doing...
dg.dataProvider = obj.ResultSet.Result
But that won't work for me, because I need to add to the dataprovider more than once. I'm getting results in batches of 10, and I need to add each batch to the dataProvider when it's received.
I also tried to to do...
var dgDP:dataProvider = new dataProvider();
But for some reason Flex doesn't recognize it...
Any ideas on how I can make this happen?
You have to initialize the dataProvider.
<mx:DataGrid creationComplete="onDGCreate(event)"/>
Script:
public function onDGCreate(e:Event):void
{
var dg:DataGrid = e.currentTarget as DataGrid;
dg.dataProvider = new ArrayCollection();
//or
dg.dataProvider = new XMLListCollection();
}
Now this will work:
dg.dataProvider.addItem(obj.ResultSet.Result[i]);
When you assign something other than ArrayCollection and XMLListCollection to the dataProvider property, it will be converted to an ICollectionView object. The only implementer of this interface is the ListCollectionView class (base class of ArrayCollection and XMLListCollection) which has addItem and addItemAt methods.
A dataProvider is a property which resides on many ListBased classes. It is not a data type. What is the data type of your dataProvider? IT can be XML, an array, an XMLListCollection, an ArrayCollection, an XMLList, or a generic object. [and I assume other data types are supported).
The 'how' you add something to your dataProvider depends entirely on the type of dataProvider you are using.
In Flex 4, the dataProvider objects must implement the IList interface, but in Flex 3 dataProviders are generic objects.
In your situation, since you already have the objects, I'd just loop over them and add them to an array or ArrayCollection and then use hat array as a dataProvider.

Flex: make getter Bindable in an value object

I have an value object in Flex which looks like:
[Bindable]
public class MyVO
{
public var a:ArrayCollection;
public var b:ArrayCollection;
private var timeSort:Sort;
public function ShiftVO(){
timeSort = new Sort();
timeSort.compareFunction = sortDates;
}
public function get times():ArrayCollection{
var ac:ArrayCollection = new ArrayCollection(a.toArray().concat(b.toArray()));
ac.sort = timeSort;
ac.refresh();
return ac;
}
It's about the getter method. I display the data of the getter in a datagrid and whenever I change some values of a or b I want to update the view as well. How do I achieve this? Currently the view doesn't update itself automatically, I have to open up the view again to see the new values.
When you make a property [Bindable], the Flex will read the getter whenever its setter is called (ie, when the property is updated); you haven't declared any setter and hence there is no way for Flex to know that the value of property has been updated.
You must define both a setter and a getter method to use the [Bindable] tag with the property. If you define just a setter method, you create a write-only property that you cannot use as the source of a data-binding expression. If you define just a getter method, you create a read-only property that you can use as the source of a data-binding expression without inserting the [Bindable] metadata tag. This is similar to the way that you can use a variable, defined by using the const keyword, as the source for a data binding expression.
May be you can define an empty setter and call it whenever you update a or b.
public function set times(ac:ArrayCollection):void { }
//somewhere else in the code:
a = someArrayCol;
/**
* this will invoke the setter which will in turn
* invoke the bindable getter and update the values
* */
times = null;
Just noticed that you're using Bindable on the class instead of the property: when you use the Bindable tag this way, it makes
usable as the source of a binding expression all public properties that you defined as variables, and all public properties that are defined by using both a setter and a getter method.
Thus, unless you define a setter, the property is not bindable even if the whole class is declared as bindable.

combobox dataprovider

I have the following:
<mx:RemoteObject id="myCFC" destination="ColdFusion" source="components.myCFC" showBusyCursor="true">
<mx:method name="getStuff" result="UserHandler(event);"/>
</mx:RemoteObject>
...
<mx:ComboBox id="propertyCode" dataProvider="{qry_stuff}" labelField="name" />
Index.as has:
[Bindable] public var qry_stuff:ArrayCollection = new ArrayCollection;
private function UserHandler(event:ResultEvent):void {
qry_stuff= event.result as ArrayCollection;
}
public function init():void {
/* call my remote Object to get my data */
myCFC.getStuff();
}
my problem is the combobox displays [object Object]
I know there is nothing wrong with the cfc and there is a field called "name" in getStuff. Why does it not display the value of the object?
thanks in advance.
There is a property on the ComboBox class called labelField. Go ahead and set that to the name field on the data that is returned. If that doesn't work - you need to debug your returned values from CF - to be sure that the name property is actually being populated on the client side as well.
In addition, you data is probably being returned as an array (not an ArrayCollection) - in which case, you would need to set:
qryStuff = ArrayCollection( event.result as Array );
Note: You probably also want to 'strong-type' your response data by creating an ActionScript value object - so that it is not just a generic 'object' that is being returned from CF. You then can use the [RemoteClass(alias="com.sample.MyCFC")] metadata tag to map that value object to your server-side VO.
In my cfc, I had to explicitly set data/label.

Binding to a read-only getter in AS3

Consider the following code:
[Bindable(event="ReportHeaderVO_effectiveFromDateJulian_updated")]
public function set effectiveFromDateJulian ( value:Number ) : void
{
_effectiveFromDateJulian = value;
dispatchEvent( new FlexEvent("ReportHeaderVO_effectiveFromDateJulian_updated") );
}
public function get effectiveFromDateJulian () : Number
{
return _effectiveFromDateJulian;
}
public function get effectiveFromDate () : Date
{
return DateUtil.convertJDEJulianToDate(_effectiveFromDateJulian);
}
There is a setter and a getter for the effectiveFromDateJulian which is a number representation of the date. I have provided a seperate getter which retrieves the same value, only converted to a proper date. It is a getter only though and relies on the setter for the numeric property to get its data from; so the effectiveFromDate property is effectively read-only.
Data binding works on the effectiveFromDateJulian property; any updates work fine and notify everything properly. But when binding to the effectiveFromDate (getter only) property, I get a warning from the compiler:
warning: unable to bind to property 'effectiveToDate' on class 'com.vo::ReportHeaderVO'
Is there a way to make it possible to bind to this read-only property? I would assume I would have to dispatch an event on the setter that effects the read-only property, but I don't know what that would look like.
This is a simple example, you could imagine a read-only property that depends on several setters to function and when any of those setters are updated the read-only property would need to fire a propertyChanged event as well. Any ideas? Please let me know if I need to clarify anything.
Update:
From the Adobe documentation here:
http://livedocs.adobe.com/flex/3/html/help.html?content=databinding_8.html
Using read-only properties as the
source for data binding
You can automatically use a read-only
property defined by a getter method,
which means no setter method, as the
source for a data-binding expression.
Flex performs the data binding once
when the application starts.
Because the data binding from a
read-only property occurs only once at
application start up, you omit the
[Bindable] metadata tag for the
read-only property.
And this makes sense for constant values, but in this case the value does change, it just doesn't get set directly.
Make the readonly getter Bindable and dispatch the corresponding event from the original setter method.
[Bindable(event="ReportHeaderVO_effectiveFromDateJulian_updated")]
public function set effectiveFromDateJulian ( value:Number ) : void
{
_effectiveFromDateJulian = value;
dispatchEvent( new FlexEvent("ReportHeaderVO_effectiveFromDateJulian_updated") );
dispatchEvent( new FlexEvent("ReportHeaderVO_effectiveFromDate_updated") );
}
[Bindable(event="ReportHeaderVO_effectiveFromDate_updated")]
public function get effectiveFromDate (date:Date) : Date
{
return DateUtil.convertJDEJulianToDate(_effectiveFromDateJulian);
}

Resources