AddItemAt() of arraycollection is not working in flex - apache-flex

I am trying to add an item in arraycollection which is sorted and filtered using addItemAt().
But addItemAt() is not adding item to the specified index.
Do anyone knows the solution for the above problem.

I am trying to add an item in arraycollection which is sorted and
filtered
If the collection is sorted, the filter will automatically be refreshed when you add a new item to it. So, the index you add your item may not the index were your item ends up. It depends entirely on the sorting algorithm.
You can remove the sort to lock your new item at the index you specify. Off the top of my head, do this:
arrayCollection.sort = null;
arrayCollection.refresh();
I'm pretty sure the same concept applies to filtering. If you have a filter applied to a collection, the new item needs to match the filter criteria or else it will not show up in the collection until the filter is removed.

I had a problem similiar to this only in Flex 3 and with a sorted ArrayCollection. If you scour, you will find that addItemAt does not work with a sorted ArrayCollection (and prolly not filtered? don't know). The item will be added according to the sort criteria.
However, I needed a sorted ArrayCollection (alpha) with a "Select All" option at the top, so this is how I proceeded:
An array can be sorted easily (array.sort), so I first created an Array. Then I Looped the ArrayCollection and added the item from the ArrayCollection on which I wished to sort to the array. This new array was then sorted.
The newly sorted array was looped and within this loop, the ArrayCollection was looped again. If a match was found on the sorted item, I added this object to a new ArrayCollection but also created a new property of the object added called "sortOrder" which was set to the loop count.
Next the "Select All" Object was created and its sortOrder set to -1.
Finally, numeric sort was created on the sortOrder field of the ArrayCollection and Voila -- it worked.
Perhaps someone has a more elegant solution but I was in a hurry and it worked darn it.
Hope this helps someone.

addItemAt() is adding item to the specified index.
*for example:
*
<fx:Script>
<![CDATA[
import mx.collections.ArrayCollection;
[Bindable]
private var myArray:ArrayCollection= new ArrayCollection([
{student:'one',subject:'2'},
{student:'two',subject:'4'},
{student:'three',subject:'5'},
{student:'four',subject:'6'}
]);
protected function addArrayCollectioninRuntime(event:MouseEvent):void
{
myArray.addItemAt({student:nameTxtinput.text,subject:subjectTxtinput.text},3);
}
]]>
</fx:Script>
<s:layout>
<s:VerticalLayout/>
</s:layout>
<s:Form>
<s:FormItem label="Student Name :">
<s:TextInput id="nameTxtinput"/>
</s:FormItem>
<s:FormItem label="Student Subject :">
<s:TextInput id="subjectTxtinput" />
</s:FormItem>
<s:Button label="Submit" click="addArrayCollectioninRuntime(event)"/>
</s:Form>
<mx:DataGrid dataProvider="{myArray}" id="dGrid" >
<mx:columns>
<mx:DataGridColumn dataField="student" id="stud"/>
<mx:DataGridColumn dataField="subject" id="sub"/>
</mx:columns>
</mx:DataGrid>

Related

How to populate flex combo box with xml data

here is the xml data :
<root>
<recommendedMaterials>
<value label="Aluminium" data="0" />
<value label="Iron" data="0" />
</recommendedMaterials>
</root
My code :
<mx:Script>
<![CDATA[
public function populateRecommendedMaterials(xml_val:XML)
{
materials_Cmb.dataProvider=(xml_val.recommendedMaterials);
}
]]>
</mx:Script>
<mx:ComboBox x="212" y="164" id="materials_Cmb" dataProvider="materialsCmb_Dp"></mx:ComboBox>
</mx:Canvas>
The problem is that the whole xml gets populated. I just want the labels. :(
There are two approaches here, depending on what you need. In either case the data you want are the children of the recommendedMaterials node, not the node itself (which is what you did).
materials_Cmb.dataProvider =
new XMLListCollection(xml_val.recommendedMaterials.children());
This should already do the trick. Note that I wrapped the XMLList in an XMLListCollection: this is not strictly necessary with the mx:ComboBox, because it will do the same internally, but for Spark components it would be mandatory.
Another more concise solution would be to just find all the 'value' nodes, but I don't know whether that approach fits your bill.
materials_Cmb.dataProvider =
new XMLListCollection(xml_val..value);
Also don't forget to assign the correct 'labelField' in the ComboBox:
<s:ComboBox labelField="#label" />
The # sign represents an XML attribute.

How to access (reference) custom itemRenderer in TileList?

I have a TileList cp with 10 item.
How can i call a function in 4. item (for example) from outside, where i created a TileList cp?
Thanks
UPDATE:
Based on your comments, this should be even easier. You would just loop through each of the rows in your List's dataProvider and make whatever changes are necessary. At the end of the function just call the refresh function on the ArrayCollection. Using my example below:
public function myFunction(evt:Event):void
{
for each (var o:MyObject in myDataProvider)
{
o.someProperty = "Updated";
}
myArrayCollection.refresh();
}
After the ArrayCollection is updated, calling the refresh function should cause the List to refresh its item renderers as well.
ORIGINAL ANSWER:
It sounds like you want to call a function outside your ItemRenderer when a button or something is clicked in the ItemRenderer but still be able to access the data for the item that was clicked.
Assuming I've got this correct, you still wouldn't need to access the ItemRenderer itself. You can just do something like this (just a rough example):
<fx:Script>
<![CDATA[
public function myFunction(evt:Event):void
{
trace(MyObject(myList.selectedItem).someProperty.toString());
}
]]>
</fx:Script>
<mx:List id="myList" dataProvider="{myDataProvider}" >
<mx:itemRenderer>
<fx:Component>
<mx:CheckBox selectedField="IsSelected" change="outerDocument.myFunction(event);" />
</fx:Component>
</mx:itemRenderer>
</mx:List>
If you do need to pass a completely separate parameter that's not stored in your List's dataProvider, just pass it as an argument to the change eventHandler.
change="outerDocument.myFunction(event, someOtherValue);"

How should one view different filtered data in different datagrids that use the same underlying ArrayCollection?

I have a global arraycollection that holds all the information pertaining to a type of data set. I would like to have different datagrids that filter the data, typically by equating a particular column. How should one go about this?
Edit: My thinking so far:
If there was a filter function for the datagrid rather than the underlying arraycollection, that would solve the problem. Alternatively, if one could reflect the global arraycollection with a "subset arraycollection" that automatically filtered the global arraycollection, and of course, automatically reflected changes in the underlying array collection, that would do it too. Is either of these solutions natural/trivial in flex?
You should use a ListCollectionView for this.
It allows custom filters of an underlying source collection. Changes to the source collection are reflected in the filtered view.
Ie:
[Bindable]
public var allTheData:ArrayCollection;
<mx:ListCollectionView list="{allTheData}" filterFunction="myFilterFunction" id="filteredView1" />
<mx:DataGrid dataProvider="{filteredView1}" />
Here's one way to do it. Although this example uses Lists, it should work for DataGrids since it's the collection being filtered and not the view. The 'Add List Items' button and addListItems() method show one way to update the filtered Lists when the underlying data changes.
<fx:Script>
<![CDATA[
private function populateListA(collection:ArrayCollection):ArrayCollection
{
var ac:ArrayCollection = new ArrayCollection(collection.source);
ac.filterFunction = filterListA;
ac.refresh();
return ac;
}
private function populateListB(collection:ArrayCollection):ArrayCollection
{
var ac:ArrayCollection = new ArrayCollection(collection.source);
ac.filterFunction = filterListB;
ac.refresh();
return ac;
}
private function filterListA(item:Object):Boolean
{
return item == "ListA";
}
private function filterListB(item:Object):Boolean
{
return item == "ListB";
}
private function addListItems():void
{
arrayCollection.addItem("ListA");
arrayCollection.addItem("ListB");
listA.dataProvider = populateListA(arrayCollection);
listB.dataProvider = populateListB(arrayCollection);
}
]]>
</fx:Script>
<fx:Declarations>
<s:ArrayCollection id="arrayCollection">
<fx:Array>
<fx:String>ListA</fx:String>
<fx:String>ListA</fx:String>
<fx:String>ListA</fx:String>
<fx:String>ListB</fx:String>
<fx:String>ListB</fx:String>
<fx:String>ListB</fx:String>
</fx:Array>
</s:ArrayCollection>
</fx:Declarations>
<s:layout>
<s:VerticalLayout/>
</s:layout>
<s:List id="listA"
dataProvider="{populateListA(arrayCollection)}"/>
<s:List id="listB"
dataProvider="{populateListB(arrayCollection)}"/>
<s:Button click="addListItems()"
label="Add List Items"/>

Prevent displaying of some data in last row of Flex Datagrid

I have some DataGrid with editable rows, which has also an option to add new row 'dynamically'. I mean, last row has some default data (e.g. "CLICK HERE TO ADD NEW ROW") and when user clicks on it, he can edit that value and new row will be eventually inserted.
However, I also have a column in same DataGrid which doesn't come from DataGrid's DataProvider. That column is used to delete specific row and it should only display clickable image with associated mouse click action (within custom itemRenderer).
I would like to display that image on every row except that last one.
Here is my DataGridColumn code so far:
<mx:DataGridColumn width="20" editable="false">
<mx:itemRenderer>
<mx:Component>
<mx:VBox creationComplete="cc()">
<mx:Script>
<![CDATA[
import mx.controls.Alert;
import mx.events.CloseEvent;
public function cc():void{
delImg.source = "assets/images/delete-icon.png";
}
]]>
</mx:Script>
<mx:Image id="delImg" smoothBitmapContent="true" width="15" height="15" click="outerDocument.confirmDelete(event)"/>
</mx:VBox>
</mx:Component>
</mx:itemRenderer>
</mx:DataGridColumn>
I suppose I should put some condition in my cc function and somehow compare current row's index or something with my dataProvider's length... I'm not really sure how to do that since I cannot get rowIndex property because I'm not working with DataGridEvent here...
Please help me with this one and thank you very much for any help! :)
A VBox does not implement IDropInListItemRenderer. A IDropInListItemRenderer gives you the "listData" property (which is of type "DataGridListData"). Furthermore, "listData.owner" will be the DataGrid object, and "listData.rowIndex" gives you the rowIndex property.

Flex drag and drop reorder of a HorizontalList control - Finding FROM and TO indexes?

I have a Flex3 project with 2 HorizontalList controls; one of which has drag & drop enabled. Both controls will always have the same number of items, and are related... index 0 from the 1st control matches index 0 from the 2nd control, and so on.
Naturally, when using drag and drop to reorder the 2nd control, I want the items in the 1st control to reflect the same reordering. I think I can handle the actual updating of the control, but I'm having trouble finding the from and to indexes of the item that has been moved via drag & drop.
Using the dragDrop method on the control that allows drag and drop, and perusing the contents of event.currentTarget (instead of event.target because the docs say so) in the debugger shows a public property named selectedIndex which does seem to hold the from index, but I don't see any properties that will tell me the to index.
Am I overlooking the property? Do I have to infer it based on something else? Is there a better way to do this?
update: An acceptable solution might also be to iterate over the contents of the HorizontalList and save the internal index value to an array that could then be used to re-sort the dataProvider for the other list; but I can't figure out how to iterate over the list contents, so I'm just as stuck. Any help there?
Here's my code:
main.mxml:
<?xml version="1.0" encoding="utf-8"?>
<mx:Application
xmlns:mx="http://www.adobe.com/2006/mxml"
xmlns:com="components.*"
layout="absolute"
>
<mx:Script>
<![CDATA[
import mx.events.DragEvent;
import mx.controls.Alert;
import mx.collections.ArrayCollection;
//these two variables are junk data to represent
//what we will get back from our data services...
[Bindable]
private var btnData:ArrayCollection = new ArrayCollection(
[
{title:'title1', index:0},
{title:'title2', index:1},
{title:'title3', index:2},
{title:'title4', index:3}
]
);
[Bindable]
private var chainData:ArrayCollection = new ArrayCollection(
[
{tmp:'testing 1'},
{tmp:'testing 2'},
{tmp:'testing 3'},
{tmp:'testing 4'}
]
);
//handle button re-ordering
private function handleBtnReorder(event:DragEvent):void{
Alert.show(event.action.toString());
}
]]>
</mx:Script>
<mx:HorizontalList
id="ChainView"
dataProvider="{chainData}"
itemRenderer="components.ItemRenderers.ChainLinkRenderer"
left="20"
right="20"
top="20"
height="300"
rowHeight="300"
columnWidth="500"
rollOverColor="#ffffff"
selectionColor="#eeeeee"
/>
<mx:HorizontalList
id="btnList"
dataProvider="{btnData}"
dragEnabled="true"
dropEnabled="true"
dragMoveEnabled="true"
dragDrop="handleBtnReorder(event)"
itemRenderer="components.ItemRenderers.BtnListRenderer"
horizontalCenter="true"
left="20"
right="20"
top="320"
height="50"
rowHeight="35"
rollOverColor="#ffffff"
selectionColor="#ffffff"
/>
</mx:Application>
The Alert.show(...) isn't there to show anything useful, I've set a breakpoint on that line so that I can inspect the event argument.
I don't think the custom itemRenderer's are important (there's not much code to them, yet), so I'll leave them out for brevity's sake. If you think they are important, let me know and I'll edit them in here.
It turns out the solution was to switch from using the dragDrop event to the dragComplete event; at which point, the binded dataProvider has been updated to reflect the reordering.
Thats why the Model Locator pattern is made for!
You must move your dataProvider to a singleton class (Model for instance) than your item renderers can easily access to it. There is no point that your item has the property Index since its already given by a [ArrayCollection].getItemIndex(..)

Resources