Flex components with dynamic data update - apache-flex

I need to push real time data to a Flex GUI (Data grid), but each time new data is pushed to the grid, it's losing its previous state.
Example:
if I scrolled to the right, after the next update scrolls come back to the default position, that is, left
if I am selecting any row, it's getting unselected just after update.
Is there a way to maintain the state?
I am using Flex 3. I can move to Flex 4 if it helps.

How do you set the data? Do you change the DataGrid dataProvider with a new collection of objects ? Because, from the behavior described by you this may be the case.
The solution would be instead changing the dataProvider of the DG. Each time, you should just update the values in the collection which is assigned as data provider.
For example,
[Bindable]
var myDataCollection:ArrayCollection = new ArrayCollection([0,1,2,3,4]);
// Handle creation complete.
private function onCreationComplete():void
{
initDG();
}
// Init DG data provider just once.
private function initDG(data:ArraCollection):void
{
myDG.dataProvider = data;
}
private function updateDG_Method_1(row:int, value:int):void
{
var data:ArrayCollection = myDG.dataProvider as ArrayCollection;
if(data && data.length > row)
{
data[row] = value;
}
// We can force refresh if not not done automatically.
myDG.invalidateList();
myDG.validateNow();
}
private function updateDG_Method_2(row:int, value:int):void
{
if(myDataCollection && myDataCollection.length > row)
{
myDataCollection[row] = value;
}
// We can force refresh if not not done automatically.
myDG.invalidateList();
myDG.validateNow();
}
Please ignore/correct any typo .. since I did not test this :))
Good luck!

Related

Ensure Spark DataGrid item is visible

Is there a way to make sure the selected item is visible in the Spark DataGrid?
.
Context
I have a data grid bound to an array collection. I receive remotely a service that gives me a ID (string) of an object that is in the collection. Using just the string I loop through the collection to find the item that matches the string. I find the object by it's ID. Now I have the object I want to select in the datagrid. I can set the
dataGrid.selectedItem = object;
Now I need to make sure it's visible. I do not have the row or column index.
.
Update
Using the answer below I've complimented it with this function:
/**
* Ensures the item is visible (for spark data grid)
**/
public function ensureItemIsVisibleInSparkDataGrid(datagrid:spark.components.DataGrid, item:Object):void {
var list:IList = datagrid.dataProvider;
var length:int = list.length;
var itemFound:Boolean;
var object:Object;
var index:int;
for (var i:int;i<length;i++) {
object = list.getItemAt(i);
if (object==item) {
itemFound = true;
index = i;
break;
}
}
if (itemFound) {
datagrid.ensureCellIsVisible(index);
}
}
Yes, it's called ensureCellIsVisible(). You need to know that row and column of the item in question. To get this to work you'd need to listen for the selectionChange event then calculate the row and column of the currently selected item.

HierarchicalCollectionView: One time sort?

I have an AdvancedDataGrid that relies on a HierarchicalCollectionView as it's dataProvider. What I'd like to do is sort the data when it is first loaded, but then disable the sort so that anything that is added after the initial load doesn't cause the grid to auto-sort again. I tried something like this:
this._myDataProvider = new HierarchicalCollectionView(
new HierarchicalData(this._model.rootTasks));
var mySort:Sort = new Sort();
mySort.fields = [new SortField("startDate")];
this._tasksDataProvider.sort = taskSorting;
this._tasksDataProvider.refresh();
this._tasksDataProvider.sort = null;
But setting the sort to null just leaves the data unsorted. I guess what I'm asking is: how can I sort the underlying hierarchical data since it seems setting the sort property will keep it dynamically sorting. Thanks for any help you can provide.
Personally, I would change the sort order when you're getting the data. Either it's done on the server side or when you parse the data (ie. in your model). You can do a one time sort using Array with sortOn.
you can
1. sort the original data with sort function,
2. clone content and put it to a new collection with no sort (be careful do and make a manual clone),
3. just use the new data collection.
I had the same kind of problem until I realized that the sorting with Sort object does not change the "physical" ordering of the items within the Collection, so when you remove the Sort, the next refresh reverts the view to the actual "physical" ordering.
Similarily as stated above, I solved by it by cloning the sub-collections into sorted order this way:
public static function buildPositionSort():Sort
{
var dataSortField:SortField = new SortField();
dataSortField.name = "position";
dataSortField.numeric = true;
dataSortField.descending = false;
var sort:Sort = new Sort();
sort.fields = [ dataSortField ];
return sort;
}
/**
* This method is used to create a clone of ArrayCollection, because sorting does not
* actually change the physical ordering of the items.
*/
public static function createSortedArrayCollectionCopy(source:ArrayCollection):ArrayCollection
{
var target:ArrayCollection = new ArrayCollection();
source.sort = buildPositionSort();
source.refresh();
for each (var item:Object in source)
{
if (item.children != null) item.children = createSortedArrayCollectionCopy(item.children);
target.addItem(item);
}
return target;
}

Flex Advanced Data Grid w/ hierarchical data: How to access currentTarget fields on dragdrop event?

So this is driving me crazy. I've got an advanced data grid with a dataprovider that's an array collection w/ hierarchical data. Each object (including the children) has an id field. I'm trying to drag and drop data from within the ADG. When this happens, I need to grab the id off the drop target and change the dragged object's parentid field. Here's what I've got:
public function topAccountsGrid_dragDropHandler(event:DragEvent):void{
//In this function, you need to make the move, update the field in salesforce, and refresh the salesforce data...
if(checkActivateAccountManageMode.selected == true) {
var dragObj:Array = event.dragSource.dataForFormat("treeDataGridItems") as Array;
var newParentId:String = event.currentTarget['Id'];
dragObj[0].ParentId = newParentId;
} else {
return;
}
app.wrapper.save(dragObj[0],
new mx.rpc.Responder(
function():void {
refreshData();
},
function():void{_status = "apex error!";}
)
);
}
I can access the data I'm draggin (hence changing parentId) but not the currentTarget. I think the hierarchical data is part of the problem, but I can't find much in the documentation? Any thoughts?
event.currentTarget is not a node, it's the ADG itself. However, it's quite easy to get the information you want, since the ADG stores that data internally (as mx_internal).
I'm using the following code snippets (tested with Flex SDK 4.1) in a dragOver handler, but I guess it will work in a dragDrop handler too.
protected function myGrid_dragDropHandler(event:DragEvent):void
{
// Get the dragged items. This could either be an Array, a Vector or NULL.
var draggedItems:Object = getDraggedItems(event.dragSource);
if (!draggedItems)
return;
// That's our ADG where the event handler is registered.
var dropTarget:AdvancedDataGrid = AdvancedDataGrid(event.currentTarget);
// Get the internal information about the dropTarget from the ADG.
var dropData:Object = mx_internal::dropTarget._dropData;
// In case the dataProvider is hierarchical, get the internal hierarchicalData aka rootModel.
var hierarchicalData:IHierarchicalData = dropTarget.mx_internal::_rootModel;
var targetParent:Object = null;
// If it's a hierarchical data structure and the dropData could be retrieved
// then get the parent node to which the draggedItems are going to be added.
if (hierarchicalData && dropData)
targetParent = dropData.parent;
for each (var draggedItem:Object in draggedItems)
{
// do something with the draggedItem
}
}
protected function getDraggedItems(dragSource:DragSource):Object
{
if (dragSource.hasFormat("treeDataGridItems"))
return dragSource.dataForFormat("treeDataGridItems") as Array;
if (dragSource.hasFormat("items"))
return dragSource.dataForFormat("items") as Array;
if (dragSource.hasFormat("itemsByIndex"))
return dragSource.dataForFormat("itemsByIndex") as Vector.<Object>;
return null;
}
var dropData:Object = mx_internal::dropTarget._dropData;
should be
var dropData:Object = dropTarget.mx_internal::_dropData;
Try this.

CheckBox header renderer with HierarchicalCollectionView

I've gotten a checkbox header renderer to work well with flat DPs, but a
hierarchical collection view is another story. On click, I want it to select all
checkboxes in a given column. Here is my code:
var dp:HierarchicalCollectionView = _dataGrid.dataProvider as
HierarchicalCollectionView;
var testDp:GroupingCollection = dp.source as GroupingCollection;
var rawDp:ArrayCollection = testDp.source as ArrayCollection;
for(var i:int=0 ; i < rawDp.length ; i++){
rawDp[i][_dataField] = cb.selected;
}
It selects all checkboxes on the 2nd level of data, but doesn't select the top
level of data. What am I missing here? I can't seem to find it.
Any tips are greatly appreciated. Thank you.
For hierarchical data you have to use a cursor which iterates over all levels of the hierarchical data.
var dp:IHierarchicalCollectionView = _dataGrid.hierarchicalCollectionView;
var cursor:IViewCursor= dp.createCursor();
while (!cursor.afterLast)
{
cursor.current[_dataField] = cb.selected;
cursor.moveNext();
}
Howerver, this works only with nodes that have previously been opened. So either expand all nodes with _dataGrid.expandAll() (you can collapse them afterwards since the nodes only have to be opened once) or iterate your hierarchical data manually:
function setCheckBoxValue(children:ArrayCollection, value:Boolean):void
{
for each (var child:Object in children)
{
if (child.hasOwnProperty("children") && child["children"])
setCheckBoxValue(child["children"], value);
child[_dataField] = value;
}
}
var myDataProvider:HierarchicalData = /* your data provider */;
// Call it like this...
setCheckBoxValue(myDataProvider.source, cb.selected);
Update: To answer your second question...
Create a new CheckBoxColumn which extends AdvancedDataGridColumn. You can use it to preconfigure your headerRenderer and itemRenderer.
In your custom item renderer you get hold of your column like this:grid = AdvancedDataGrid(listData.owner);
column = grid.columns[listData.columnIndex] as CheckBoxColumn;
Do the same in your header renderer.
Whenever the CheckBox value in one of your item renderers changes dispatch a event through your column. Something like: column.dispatchEvent(new Event("checkBoxValueChanged"));
Your header render should add an event listener to the column for the "checkBoxValueChanged" event (or whatever you call it). Whenever that event is fired loop through your data provider and update the headers CheckBox accordingly.
In theory that should work. HTH

How do I force all Tree itemrenderers to refresh?

I have item renderers in an mx.controls.Tree that I need to refresh on demand.
I have code in the updateDisplayList that fires for only some of the visible nodes no matter what I do. I've tried triggering a change that they should all be listening for; I have tried clearing and resetting the dataProvider and the itemRenderer properties.
private function forceCategoryTreeRefresh(event : Event = null) : void
{
trace("forceCategoryTreeRefresh");
var prevDataProvider : Object = CategoryTree.dataProvider;
CategoryTree.dataProvider = null;
CategoryTree.validateNow();
CategoryTree.dataProvider = prevDataProvider;
var prevItemRenderer : IFactory = CategoryTree.itemRenderer;
CategoryTree.itemRenderer = null;
CategoryTree.itemRenderer = prevItemRenderer as IFactory;
_categoriesChangeDispatcher.dispatchEvent(new Event(Event.CHANGE));
}
The nodes refresh properly when I scroll them into view (e.g. the .data gets set), but I cannot force the ones that already exist to refresh or reset themselves.
Any ideas?
Try calling this function on change event:
private function refreshList(e:Event):void{
listName.invalidateSize();
listName.invalidateList();
listName.invalidateDisplayList();
listName.validateSize(true);
listName.validateNow();
}
for me work quite well
Try
treeView.dataProvider = treeView.dataProvider;
That should update Tree view.
ItemRenderers in Flex are virtualized so there will not always be one ItemRenderer for each Tree node.
However, you can invalidate the nodes to force a refresh. The answer to this question gives an example of how to do that.

Resources