Why isn't there an addAll() function available to ArrayCollections in Actionscript? - apache-flex

Is there any reason why there isn't an addAll() function available when using ArrayCollections in Actionscript? I've been using for loops to individually add them one at a time using addItem(). Is this common or is there a more convenient way?

I can add some historical nuance to this. Apparently, Adobe heard the Flex community and has responded. An addAll(addList:IList) method has been added to the ListCollectionView type in the Flex 3.4 SDK.
But just in case there are others who may still be looking for a one-liner equivalent that works across the board, here is my one-very-long-line go at it:
var arrColl1 = new ArrayCollection(['x','y','z']);
var arrColl2 = new ArrayCollection(['a', 'b', 'c']);
// Using Flex SDK version 3.4:
arrColl1.addAll( arrColl2 );
// Using any Flex SDK:
arrColl2.source.forEach(function(item:*, i:int, arr:Array):void { arrColl1.addItem(item); }, this);
This is essentially what the Flex implementation does, and should handle binding concerns correctly, although it is not necessarily the prettiest thing to look at.

There's no reason that it isn't there, it's just not there. If you changed your code to use a plain Array instead of an ArrayCollection, you can use the Array.concat method. Otherwise, the only option is addItem in a loop.

By way of example, try dropping this into a container. Essentially, it seems that if you have to have a one liner, creating a new ArrayCollection with the source of the original plus the new data will work, at least in the case below. Attempts to manipulate the source directly don't seem terribly useful, at least for data binding purposes (and if no data binding or events are involved, its probably better to use an array anyway).
<mx:Script>
<![CDATA[
import mx.collections.ArrayCollection;
[Bindable] public var collection:ArrayCollection = new ArrayCollection([
2,6,4,6,7,8,9
]);
public function addToCollection():void {
collection.addItem(Number(collectionValue.text));
}
public function newCollectionWithAddition():void {
collection = new ArrayCollection(collection.source.concat(Number(collectionValue2.text)));
}
public function addToCollectionSource():void {
collection.source.push(Number(sourceValue));
}
public function addToCollectionSourceWithRefresh():void {
collection.source.push(Number(sourceValue2));
collection.refresh();
}
]]>
</mx:Script>
<mx:HBox width="100%">
<mx:PieChart width="300" height="300">
<mx:series>
<mx:PieSeries dataProvider="{ collection }" />
</mx:series>
</mx:PieChart>
<mx:PieChart width="300" height="300">
<mx:series>
<mx:PieSeries dataProvider="{ collection.source }" />
</mx:series>
</mx:PieChart>
</mx:HBox>
<mx:HBox>
<mx:TextInput id="collectionValue" />
<mx:Button label="Add To ArrayCollection"
click="addToCollection()"
/>
</mx:HBox>
<mx:HBox>
<mx:TextInput id="collectionValue2" />
<mx:Button label="Create New ArrayCollection with new value"
click="newCollectionWithAddition()"
/>
</mx:HBox>
<mx:HBox>
<mx:TextInput id="sourceValue" />
<mx:Button label="Add To ArrayCollection Source"
click="addToCollectionSource()"
/>
</mx:HBox>
<mx:HBox>
<mx:TextInput id="sourceValue2" />
<mx:Button label="Add To ArrayCollection Source with refresh"
click="addToCollectionSourceWithRefresh()"
/>
</mx:HBox>

After some more investigation, it turns out that an ArrayCollection is just a wrapper for an Array and that you have access to the array via arrcoll1.source. This allows you to call concat. I was able to remove my for loop and use this instead.
var arrColl1 = new ArrayCollection(['x','y','z']);
var arrColl2 = new ArrayCollection(['a', 'b', 'c']);
arrColl1.source = arColl1.source.concat(arrColl2.source);
If you are concatenating an array, for example selectedItems in a list, you can do this:
arrColl1.source = arrColl1.source.concat(seletedItems);

Related

Adobe Air: need help transferring data from sqlite to components

I have successfully received data from sqlite. I see that the data appear in trace function, however I am having a problem with displaying these data correctly. I think it has something to do with binding objects. I must be missing something somewhere, so please correct where I am wrong. Also, this question is actually a continuation of Adobe Air: drag & drop grouped components
Anyway, here is my ItemRenderer:
<?xml version="1.0" encoding="utf-8"?>
<s:ItemRenderer xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx"
autoDrawBackground="true">
<fx:Script>
[Bindable]
public var tree:TreeClass_Full;
[Bindable]
public var dataText:String = "empty";
public function setTest():void{
resultTextArea.text = "AAA";
}
</fx:Script>
<s:HGroup x="5" y="5" width="350" height="150" horizontalAlign="center" verticalAlign="middle">
<mx:Image id = "resultImage" width="100" height="100"/>
<s:TextArea id="resultTextArea" height="141" editable="false"
text = "{tree.Common_Name}"/>
</s:HGroup>
</s:ItemRenderer>
Here is the function which will be invoked after sqlite query is successful:
[Bindable]
public var dragDropLeft:ArrayCollection = new ArrayCollection();
[Bindable]
public var dragDropRight:ArrayCollection = new ArrayCollection();
protected function ResultFill():void
{
// TODO Auto-generated method stub
if (arr != null){
for (var i:int = 0; i < arr.length; ++i){
var tc:TreeClass_Full = arr[i];
var rr:resultRenderer = new resultRenderer();
rr.tree = tc;
trace("--Common Name:" + tc.Common_Name);
rr.dataText = tc.Common_Name;
rr.resultImage.source = tc.Picture1;
rr.resultTextArea.text = tc.Common_Name;
trace("----Common Name:" + rr.resultTextArea.text);
dragDropLeft.addItem(rr);
}
}
}
After the sqlite query is successful, the above function will be invoked. This is where I put in the information of my Object (my ItemClass from sqlite) into my ItemRenderer.
My ItemRenderer has two components: Image and TextArea. sqlite contains the respective image, which will be used for Image. For TextArea, all the displayable text from the database will be put there - meaning strings from multiple fields of the database will all be put together there. (but for now, I just want only one field in there, which is Common_Name)
After that, I put these ItemRenderers into my list (dragDropLeft, in this case). However, the data does not appear on the program. Yet, trace function outputs the text correctly.
I've tried a few things and I left some of the codes intact, and all of them yield the same result: the text in the TextArea does not change at all. What have I forget here?
UPDATE:
This code here covers the place that I used my Bindable ArrayCollection:
<s:HGroup width="100%" height="85%" verticalAlign="middle">
<s:List dataProvider="{dragDropLeft}" width = "45%" height = "95%"
dragEnabled="true" dragMoveEnabled="true" dropEnabled="true"
itemRenderer="resultRenderer"/>
<s:List dataProvider="{dragDropRight}" width = "45%" height = "95%"
dragEnabled="true" dragMoveEnabled="true" dropEnabled="true"
itemRenderer="resultRenderer"/>
</s:HGroup>
Basically, I tried to manipulate my ItemRenderer on my own by giving it values from sqlite, but from one of the commentors, it seems this is not correct. So how do we do this? How can we send in the data to the ItemRenderer objects that will be added in the list?
As I said in the comments: List is not a container. You cannot just add children to it. You should add data objects to its dataProvider. Basically the whole ResultFill method should be reduced to this:
dragDropLeft.dataProvider = new ArrayCollection(arr);
The ItemRenderers that represent this data will be created internally by the List component. Inside your custom ItemRenderer you can now access this data through the data property, so it would become something like this:
<s:ItemRenderer xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
autoDrawBackground="true">
<s:HGroup x="5" y="5" width="350" height="150"
horizontalAlign="center" verticalAlign="middle">
<s:Image source="{data.Picture1}" width="100" height="100"/>
<s:TextArea text="{data.Common_Name}" height="141" editable="false" />
</s:HGroup>
</s:ItemRenderer>
Note that I am assuming that the properties of TreeClass_Full are bindable for this example.

Sorting columns in a flex datagrid

The datagrid gets its data from a back end database which has records like
RecordID Division Department Date_Report_Submitted
1. Finance Accounting 11/1/2010
2. Engineering Design 4/2/2011
3. Engineering Implementation 4/2/2011
4. Support Chat_Support 2/4/2010
Clicking on the headers in the Datagrid column(Department) results in a sort based on recordID like
Division Department Date_Report_Submitted
Finance Accounting 11/1/2010
Engineering Design 4/2/2011
Engineering Implementation 4/2/2011
Support Chat_Support 2/4/2010
whereas I want it to be sorted alphabetically for the Datagrid column(Department) like
Division Department Date_Report_Submitted
Finance Accounting 11/1/2010
Support Chat_Support 2/4/2010
Engineering Design 4/2/2011
Engineering Implementation 4/2/2011
since Accounting should come before Chat_Support as per lexicographical order.
Looked at http://blog.flexexamples.com/2008/04/09/creating-a-custom-sort-on-a-datagrid-control-in-flex/#more-590 and have something like
<mx:DataGrid id="myRecords" dataProvider="{myRecords_dp}" width="810" height="274"
itemClick="getRecordData(event)">
<mx:columns>
<mx:DataGridColumn id="firstCol" width="180" fontFamily="Arial" fontSize="12"
wordWrap="true" />
<mx:Button label="Click to Sort" click="mysort()" />
</mx:columns>
</mx:DataGrid>
and
private function mysort():void
{
var sortField:SortField = new SortField();
sortField.compareFunction = mycompare;
sortField.descending = false;
var sort:Sort = new Sort();
sort.fields = [sortField];
myRecords.sort = sort;
myRecords.refresh();
}
private function mycompare(lhs:Object, rhs:Object):int
{
var valueA:String = lhs.text();
var valueB:String = rhs.text();
return ObjectUtil.stringCompare(valueA, valueB);
}
I get errors like
1061: Call to a possibly undefined method refresh through a reference with static type mx.controls:DataGrid.
for myRecords.refresh();
and
Access of possibly undefined property sort through a reference with static type mx.controls:DataGrid.
for myRecords.sort
Any suggestions would be appreciated.
The code in your question is a bit of a mess. For example, you cannot use a button as a column inside a DataGrid. That gives a compiler error.
Nevertheless, I put together a sample, based off your sample data and the code you shared, in order to show you that DataGrid sorting does not work the way you claim it does.
Play with it here.
<?xml version="1.0" encoding="utf-8"?>
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx"
minWidth="955" minHeight="600" viewSourceURL="srcview/index.html">
<fx:Declarations>
<!-- Place non-visual elements (e.g., services, value objects) here -->
</fx:Declarations>
<fx:Script>
<![CDATA[
import mx.collections.ArrayCollection;
[Bindable]
public var myRecords_dp : ArrayCollection = new ArrayCollection([
{recordID:1, division:'Finance', department:'Accounting', Date_Report_Submitted:new Date(2010,11,1)},
{recordID:2, division:'Engineering', department:'Design', Date_Report_Submitted:new Date(2010,4,2)},
{recordID:3, division:'Engineering', department:'Implementation', Date_Report_Submitted:new Date(2011,4,2)},
{recordID:4, division:'Support', department:'Chat_Support ', Date_Report_Submitted:new Date(2010,2,4)},
])
public function mysort():void{
}
public function getRecordData(event:Event):void{
}
]]>
</fx:Script>
<mx:DataGrid id="myRecords" dataProvider="{myRecords_dp}" width="810" height="274"
itemClick="getRecordData(event)">
<mx:columns>
<mx:DataGridColumn id="firstCol" width="180" fontFamily="Arial" fontSize="12"
wordWrap="true" dataField="division" />
<mx:DataGridColumn id="secondCol" width="180" fontFamily="Arial" fontSize="12"
wordWrap="true" dataField="department" />
<mx:DataGridColumn id="thirdCol" width="180" fontFamily="Arial" fontSize="12"
wordWrap="true" dataField="Date_Report_Submitted" />
<!-- <mx:Button label="Click to Sort" click="mysort()" />-->
</mx:columns>
</mx:DataGrid>
</s:Application>
To get more help with your issue; you're going to have to share some real code to demonstrate some issue; not a mish-mash of non-compileable stuff you found on the Internet.
For the issues:
1061: Call to a possibly undefined method refresh through a reference
with static type mx.controls:DataGrid. for myRecords.refresh(); and
Access of possibly undefined property sort through a reference with
static type mx.controls:DataGrid. for myRecords.sort
Try casting the dataProvider to an ArrayCollection or XMLListCollection (Whichever you're using):
ArrayCollection(myRecords.dataProvider).sort = sort;
ArrayCollection(myRecords.dataProvider).refresh();

Flex ItemRenderer Issue

Am using Checkbox as ItemRenderer in tilelist. Am trying to setting
checkbox selected values through xml. I got the values perfectly.. but
checkbox could not bind the values(could not accept that). It's
automatically sets true for all checkboxes.
This is my xml
<PmhTreeAllow>
<PmhTreeAllowname id='1' label ='Allow Text' isField='false'/>
<PmhTreeAllowname id='2' label ='Document Link' isField='false'/>
<PmhTreeAllowname id='3' label ='Test Results Entry'isField='false'/>
<PmhTreeAllowname id='4' label ='Dummy' isField='false'/>
</PmhTreeAllow>
My Tilelist..
<mx:TileList id="tileList" width="160" height="100%" textAlign="left" horizontalScrollPolicy="off" verticalScrollPolicy="off" dataProvider="modelInstance.optionCollList}" columnCount="1" backgroundAlpha="0" borderStyle="none"itemRenderer="com.Frontend.views.treeStructure.myTileList" useRollOver="false" rowHeight="28" itemClick="tileItemClick(event)" columnWidth="150" selectedIndex="0" x="10" y="0">
Check box ItemRenderer..
<?xml version="1.0" encoding="utf-8"?>
<mx:CheckBox xmlns:mx="http://www.adobe.com/2006/mxml" label="{data.#label}" selected="data.#isField}"/>
Thanks in Advance
Ashok
http://www.switchonthecode.com/tutorials/flex-using-item-renderers
This will help u..
For performance reasons, it is considered a bad practice to use binding inside an itemRenderer. Instead listen to the FlexEvent.DATA_CHANGE and manually modify your values. I Bet doing so will solve your issue.
Try an itemRenderer like this:
<mx:CheckBox xmlns:mx="http://www.adobe.com/2006/mxml" label="{data.#label}" selected="data.#isField}" dataChange="onDataChange()">
<mx:Script><[[
public function onDataChange():void{
var dataAsXML = data as XML;
this.selected = data.#isField
this.label = data.#label
]]></mx:Script>
</mx:CheckBox>
I don't do a lot with XML, but I suspect that the XML properties will not bind because XML is not like an ActionScript object and therefore the "propertyChanged" Binding events do not exist on the XML object the same way they would be on an AS3 object.

Flex: Components bound to empty ArrayCollection at load time don't render as expected when the ArrayCollection is updated

I'm new to Flex and am using TileList bound to an ArrayCollection. The array collection is empty at load time, and then updates with the results from am HTTPService call. The problem is that the item renderers aren't being rendered as expected, I'm guessing because there was no data when they were first rendered at load time. Here's simplified example:
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" >
<mx:Script>
<![CDATA[
import mx.collections.ArrayCollection;
[Bindable]
public var myList1:ArrayCollection = new ArrayCollection();
[Bindable]
public var myList2:ArrayCollection = new ArrayCollection([{item:"foo"}, {item:"bar"}]);
public function updateMyList():void
{
myList1.source = [{item:"foo"}, {item:"bar"}];
}
]]>
</mx:Script>
<mx:Button id="myButton" label="Update My List"
click="updateMyList();"/>
<mx:TileList dataProvider="{myList1}"
direction="vertical"
width="800" >
<mx:itemRenderer>
<mx:Component >
<mx:Canvas backgroundColor="yellow" >
<mx:Label text="{data.item}" width="800" />
</mx:Canvas>
</mx:Component>
</mx:itemRenderer>
</mx:TileList>
<!-- This one renders as expected -->
<mx:TileList dataProvider="{myList2}"
direction="vertical"
width="800" >
<mx:itemRenderer>
<mx:Component >
<mx:Canvas backgroundColor="yellow" >
<mx:Label text="{data.item}" width="800" />
</mx:Canvas>
</mx:Component>
</mx:itemRenderer>
</mx:TileList>
</mx:Application>
You will notice that the second TileList whose bindings has data at load time renders as expected (800px wide), bit the first TileList is rendered is not the correct width and has scrollbars around it.
Could anyone explain why this is happening or even provide some work arounds to avoid this?
Regards,
Chris
It's likely that this section is causing the problems:
public function updateMyList():void
{
myList1.source = [{item:"foo"}, {item:"bar"}];
}
From here:
source of data in the ArrayCollection.
The ArrayCollection object does not
represent any changes that you make
directly to the source array. Always
use the ICollectionView or IList
methods to modify the collection.
This property can be used as the
source for data binding. When this
property is modified, it dispatches
the listChanged event.
So I'd probably change the line to:
myList1= new ArrayCollection([{item:"foo"}, {item:"bar"}]);
http://livedocs.adobe.com/flex/3/langref/mx/controls/TileList.html
Check the API.
Set the columnWidth and rowHeight properties like this,
<mx:TileList dataProvider="{myList1}"
direction="vertical"
width="800" columnWidth="800" rowHeight="25">
There is probably a more "proper" way to do it, but that should get you started.

Is it safe to use a component reference in mxml

Consider the following radio button example.
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml">
<mx:Script>
<![CDATA[
private function getRb1():RadioButton {
trace(rb1 == null);
return rb1;
}
]]>
</mx:Script>
<mx:VBox>
<mx:RadioButtonGroup **id="rbg" selection="{getRb1()}**"/>
<mx:RadioButton id="rb1" label="Radio Button 1" />
<mx:RadioButton id="rb2" label="Radio Button 2" />
<mx:RadioButton id="rb3" label="Radio Button 3" />
</mx:VBox>
</mx:Application>
The problem is that I can not refer to rb1 while defining RadioButtonGroup, rb1 is null at that time, but i can use the selectedValue to set the initial selction.
I was just wondering is this some special case or its not safe to refer to components in mxml in general.
Thanks,
I'm not quite sure what you're asking, but hopefully this'll answer your question -- from the Flex docs:
RadioButtonGroup.selectionContains a reference to the currently
selected RadioButton control in the
group. You can access the property in
ActionScript only; it is not settable
in MXML. Setting this property to null
deselects the currently selected
RadioButton control.
In general, though, making component references in MXML is totally fine; that's how effects are often handled, among many other things. For example:
<mx:Glow id="g" />
<mx:Label showEffect="{g}" />
However in your case, assuming you're having trouble setting the selected item, it might be because you haven't specified the group attribute on the radio buttons; omitting that detaches the group component from the individual buttons. Once you add that, you can bind the group's selection property using a Bindable variable containing a reference to a component, like so:
<mx:Script>
<![CDATA[
[Bindable]
private var selectedRadioButton:RadioButton;
private function this_creationComplete(event:Event):void
{
selectedRadioButton = rb1;
}
private function btn_click(event:Event):void
{
selectedRadioButton = rb2;
}
]]>
</mx:Script>
<mx:VBox>
<mx:RadioButtonGroup id="rbg" selection="{selectedRadioButton}" />
<mx:RadioButton id="rb1" group="{rbg}" label="Radio Button 1" />
<mx:RadioButton id="rb2" group="{rbg}" label="Radio Button 2" />
<mx:RadioButton id="rb3" group="{rbg}" label="Radio Button 3" />
<mx:Button label="Choose a Different Button" click="btn_click(event)" />
</mx:VBox>
Does this make sense? Hopefully it's not completely off the mark; post back and let me know and I'll try to help out as best I can.
Generally: just because a control was declared in MXML does not mean it is available at runtime (it might be deleted from AS, not created yet, not added to stage, therefore some properties are not available yet). This indicates it is not safe to access components at runtime and depend on values.

Resources