ArrayCollection removing sort - apache-flex

After applying a numeric sort to my dataprovider(Array Collection), I can not reorder the items via a tilelist. Do I need to remove the sort from the arrayCollection. If so, is it just a case of setting collection.sort = null ?
var sortField:SortField=new SortField();
sortField.name="order";
sortField.numeric=true;
var sort:Sort=new Sort();
sort.fields=[sortField];

Setting the sort to null should indeed remove the sort for the collection. You might need to do an optional refresh().

I got caught with this problem too, I found your question, and I still didnt get it solved like Christophe suggested.
After suffering with this for a while, I discovered one way to avoid the problems you mentioned.
Simply use an auxiliary ArrayCollection to do the sort. Anyway your Sort instance seems to be temporary (you want to through it away), so why not use a temporary ArrayCollection?
Here's how my code looked like:
// myArrayCollection is the one to sort
// Create the sorter
var alphabeticSort:ISort = new Sort();
var sortfieldFirstName:ISortField = new SortField("firstName",true);
var sortfieldLastName:ISortField = new SortField("lastName",true);
alphabeticSort.fields = [sortfieldFirstName, sortfieldLastName];
// Copy myArrayCollection to aux
var aux:ArrayCollection = new ArrayCollection();
while (myArrayCollection.length > 0) {
aux.addItem(myArrayCollection.removeItemAt(0));
}
// Sort the aux
var previousSort:ISort = aux.sort;
aux.sort = alphabeticSort;
aux.refresh();
aux.sort = previousSort;
// Copy aux to myArrayCollection
var auxLength:int = aux.length;
while (auxLength > 0) {
myArrayCollection.addItemAt(aux.removeItemAt(auxLength - 1), 0);
auxLength--;
}
It's not the neatest code, it has some weird hacks like auxLength instead of aux.length (this one gave me -1 array range exception), but at least it solved my problem.

Source
Adobe Flex - Sorting an ArrayCollection by Date
/**
* #params data:Array
* #return dataCollection:Array
**/
private function orderByPeriod(data:Array):Array
{
var dataCollection:ArrayCollection = new ArrayCollection(data);//Convert Array to ArrayCollection to perform sort function
var dataSortField:SortField = new SortField();
dataSortField.name = "period"; //Assign the sort field to the field that holds the date string
var numericDataSort:Sort = new Sort();
numericDataSort.fields = [dataSortField];
dataCollection.sort = numericDataSort;
dataCollection.refresh();
return dataCollection.toArray();
}

Related

Issue with relation when creating record

I have two tables that are related as follows:
PMLprojects ONE - MANY Inovice_stat
I have a script to create a record in the Invoice_stat table. It goes as follows:
var myProjectList = app.datasources.PMLprojects;
var myProjectListID = myProjectList.Id;
var myDatasource = app.datasources.Invoice_stat;
var myCreateDatasource = myDatasource.modes.create;
now = new Date();
var draft = myDatasource.modes.create.item;
draft.EmailStatus = "Yes";
draft.PaidStatus = "No";
draft.DateCreate = now;
myCreateDatasource.createItem(function(newRecord) {
var key = newRecord._key;
});
myDatasource.saveChanges();
All the fields are properly populates except the relation to PMLprojects. How can I related the record from Invoice_stat to PMLprojects? I'm getting the following message:
Error log :
com.google.apps.appmaker.client.datasource.AbstractModelDataSource
WARNING: Could not select element with key RecordKey{key=private$6,
model
key=1Y8Ijd68IZyWFllY3d_C9fhAOFtVgKCtH|Gu5LnmmFmZHfEbrL5Ug1fybNaVLSEPn6}.
No records bound.
Here is some proposed edited code for you to try. However, do remember that if your PMLprojects datasource is not loaded on the client, then this will still fail. I also highly recommend that you check out the official documentation here https://developers.google.com/appmaker/models/relations#modify_associations.
var myProjectList = app.datasources.PMLprojects.item; //change this line to point to an item in the datasource
//var myProjectListID = myProjectList.Id; This line is not necessary
var myDatasource = app.datasources.Invoice_stat;
var myCreateDatasource = myDatasource.modes.create;
now = new Date();
var draft = myCreateDatasource.item; //you already declared the create mode
draft.EmailStatus = "Yes";
draft.PaidStatus = "No";
draft.DateCreate = now;
draft.YourRelationToPMLprojects = myProjectList; //here is where you create your relation, replace YourRelationToPMLprojects with your actual relation name should show up in code autocomplete
myCreateDatasource.createItem(function(newRecord) {
var key = newRecord._key;
});
myDatasource.saveChanges();
Since you are probably using both tables with the Manual Save mode... then #MarkusMalessa's approach might return you an error. If that is so, you have to make sure that you create the relation after you create the item but before you save changes. For that, take into consideration the following example:
var project = app.datasources.PMLprojects.item; //project item
var ds = app.datasources.Invoice_stat;
var createDs = ds.modes.create;
var draft = createDs.item;
draft.EmailStatus = "Yes";
draft.PaidStatus = "No";
draft.DateCreate = new Date();
createDs.createItem(function(){
ds.item.PMLproject = project; //here is where you create your relation
ds.saveChanges();
});
Just remember, this will only work as long as the PMLprojects datasource has already been loaded, otherwise you will probably get an error.

Sort collection and sort remains in place when adding to collection?

When I get a collection back from the service tier, I create an ArrayCollection and apply a sort. When I add an item to the collection later on, the sort is still in place? It seems to be the case. I thought I was only sorting it once, not applying a sort that will stick???
Here is the method for adding an item:
private function onAddNewClick():void
{
var fileTemplate:FileTemplateDetailDTO = new FileTemplateDetailDTO();
fileTemplate.fileTemplateId = 0;
fileTemplate.fileTmpltDtlId = 0;
fileTemplate.createdBy = appModel.userName;
newFieldsDp.addItem( fileTemplate );
this.fieldsGridEmpty.rowCount = newFieldsDp.length + 1;
totalCount = newFieldsDp.length;
this.fieldsGridEmpty.scrollToIndex( totalCount );
}
And here is what I'm trying now for when the data comes back from the service:
for each( var dto:FileTemplateCompositeDTO in coll ){
dto.templateHistory = createHistoryColl( dto.details );
var sortedArray:ArrayCollection = sortDetails( dto.details );
sortedArray.sort = null;
var freshArray:Array = sortedArray.toArray();
dto.details.source = freshArray;
model.fileTemplateComposites.addItem( FileTemplateCompositeDTO( dto ) );
}
Easiest way to do a one time sort:
var array:Array = yourArrayCollection.toArray():
array.sortOn("SomePropertyAvailableInEachItem", Array.DESCENDING | Array.NUMERIC);
yourArrayCollection.refresh();
Since you're not sorting the underlying Array, and not the collection.
No. You should call myCollection.refresh() every time it is changed. But you can listen for collectionChange event and call refresh from there:
private function collectionChangeHandler(event:CollectionEvent):void
{
if (event.kind == CollectionEventKind.ADD || event.kind == CollectionEventKind.REMOVE ||
event.kind == CollectionEventKind.REPLACE || event.kind == CollectionEventKind.MOVE || event.kind == CollectionEventKind.UPDATE)
{
ArrayCollection(event.currentTarget).refresh();
}
}
The sort of an ArrayCollection will remain linked to it. You can try to set the sort attribute to null after the initial sort and refresh, but I'm not 100% sure that a following refresh() will keep the elements order (although it should).
Another approach: after applying the initial sort to your collection, you can call its toArray() method to obtain a (sorted) array, then just create a new ArrayCollection passing that array as its source in the constructor. Note that ArrayCollection is really just a wrapper on array, so the toArray() and the construction of a new ArrayCollection for an existing array should not be heavy operations.
A Sort on an ArrayCollection is an object itself. When you assign the Sort object to the sort property on the ArrayCollection, it remains assigned until you remove it manually. To remove it, just set the sort property on your ArrayCollection to null after you call refresh().
myArrayCollection.sort = mySort;
myArrayCollection.refresh();
myArrayCollection.sort = null;
Now any time you call refresh() on the ArrayCollection, the sort is no longer in place.

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;
}

Problem converting Array to Arraylist for dataProvider

private var MealsListResult:ArrayList = new ArrayList;
protected var _data:resultData = new resultData;
private function resultHandler():void
{
var Meals:Array = _data.Meals;
MealsListResult = _data.Meals as ArrayList;
MealDataGrid.dataProvider = Meals;
MealListView.dataProvider = MealsListResult;
}
Should this be working? the MealDataGrid is populating based on the array, but I am debugging and MealsListResult is null. but _data.Meals is not and I dunno if I'm missing something simple.
I can get it to work by doing it like: var MealsListResult2:ArrayList = new ArrayList(Meals); but I feel as though the first method should be working as well!
(there's mxml list and datagrid and such not shown here of course)
if _data.Meals is its runtime type is an array then _data.Meals as ArrayCollection will failed. but, new ArrayCollection(_data.Meals as Array) will working fine.
CMIIW
i guess your problem is you can't use single object as 2 or more different ui dataprovider.
try to use
MealDataGrid.dataProvider = _data.Meals;
MealListView.dataProvider = ObjectUtils.clone(_data.Meals);
UPDATE:
sorry i miss readed, i though it was ArrayColletion. but all you need to do is the same like ArrayCollection

Combine/merge Dynamic Objects in AS3

I have 2 dynamic objects and I want to build one to contain all the properties:
var o1:Object = {prop1:val1,prop2:val2,prop3:val3};
var o2:Object = {prop3:val3a,prop4:val4};
and I need to obtain a third object that looks like that:
{prop1:val1, prop2:val2, prop3:val3a, prop4:val4};
Basically I need a way to iterate through the object properties and to add new properties to the third object. I have to mention I'm quite new to AS3/Flash/Flex.
First question, do you really mean to have prop3 in both objects? you will need to decide what to do in case of a collision like that, which object has precedence.
Secondly, check out the introspection apis: http://livedocs.adobe.com/flex/3/html/help.html?content=usingas_8.html
something like this should work:
public function mergeDynamicObjects ( objectA:Object, objectB:Object ) : Object
{
var objectC:Object = new Object();
var p:String;
for (p in objectA) {
objectC[p] = objectA[p];
}
for (p in objectB) {
objectC[p] = objectB[p];
}
return objectC;
}
If the property exists in A and B, B's will overwrite A's. Also note that if the values of a property is an object, it will pass a reference, not a copy of the value. You might need to clone the object in those cases, depending on your needs.
Note: I haven't actually tested the above, but it should be close. Let me know if it doesn't work.
Updated to fix the errors. Glad it works for you though.
You can dynamically access/set properties on objects with the index operator. The for loop will itterate over the property names, so if you put it all together, the following test passes:
[Test]
public function merge_objects():void {
var o1:Object = {prop1:"one", prop2:"two", prop3:"three"};
var o2:Object = {prop3:"threeA", prop4:"four"};
var o3:Object = new Object();
for (var prop in o1) o3[prop] = o1[prop];
for (var prop in o2) o3[prop] = o2[prop];
assertThat(o3.prop1, equalTo("one"));
assertThat(o3.prop2, equalTo("two"));
assertThat(o3.prop3, equalTo("threeA"));
assertThat(o3.prop4, equalTo("four"));
}
you can iterate over the object properties like:
var obj1:Object = new Object();
for(var str:String in obj2){
obj1[str] = "any value"; // insert the property from obj2 to obj1
}

Resources