flex select value from Combo - apache-flex

My goal is to create a generic function that selects a value in a combobox accoring to a value.
(My comoBox holds arrayCollection as dataProvider.)
The difficulty is infact to get a propertyname in runtime mode
public function selectComboByLabel(combo:ComboBox , propetryName:String, value:String):void {
var dp:ArrayCollection = combo.dataProvider as ArrayCollection;
for (var i:int=0;i<dp.length;i++) {
if (dp.getItemAt(i).propertyName==value) {
combo.selectedIndex = i;
return;
}
}
}
the line if (dp.getItemAt(i).propertyName==value)
is of course incorrect.
It should be arther something like: dp.getItemAt(i).getPropertyByName(propertyName)
Any clue on how to that ?

Don't use Object Property notation. Do this:
dp.getItemAt(i)[propertyName]

In addition to what Flextras said, you could also redo your for loop to make it easier to read:
for each(var item:Object in dp) {
if(item[propertyName] == value) {
combo.selectedItem = item;
return;
}
}

Related

Flex DatagridColumn LabelFunction Additonal Parameters

I have a datagridcolumn where a labelFunction is defined:
private function myLabelFunction(item:Object, column:DataGridColumn):String
{
var returnVal:String;
var nm:NumericFormatter;
nm.decimalSeparatorTo = ".";
nm.precision = additionalParameter;
returnVal = nmTwoDecimals.format(item[column.dataField]);
if (returnVal == '0.00')
{
returnVal = '';
}
return returnVal;
}
Would it be possible to add an additional parameter so that I could pass the property values for the formatter which I intend to use?
Like for example:
private function myLabelFunction(item:Object, column:DataGridColumn, precisionParam:int):String
{
var returnVal:String;
var nm:NumericFormatter;
nm.decimalSeparatorTo = ".";
nm.precision = precisionParam;
returnVal = nmTwoDecimals.format(item[column.dataField]);
if (returnVal == '0.00')
{
returnVal = '';
}
return returnVal;
}
Thanks.
You would have to extend the DataGridColumn class. After creating your new class simply override the existing itemToLabel function:
public function itemToLabel(data:Object):String
{
if (!data)
return " ";
if (labelFunction != null)
return labelFunction(data, this);
if (owner.labelFunction != null)
return owner.labelFunction(data, this);
if (typeof(data) == "object" || typeof(data) == "xml")
{
try
{
if ( !hasComplexFieldName )
data = data[dataField];
else
data = deriveComplexColumnData( data );
}
catch(e:Error)
{
data = null;
}
}
if (data is String)
return String(data);
try
{
return data.toString();
}
catch(e:Error)
{
}
return " ";
}
The line 'return labelFunction(data, this);' is what calls the labelFunction (will also check the owner datagrid for a labelfunction). 'data' in 'itemToLabel' is your object. You could either include the precision value you want in the object or hard code it in the extended class (or inject, or singleton, class var, whatever you like).
At this point you can go ahead and pass it as a third parameter to your new labelFunction.
In your label function for the datagrid column, you can access the assigned data field by using the dataField property, see the following syntax below:
"supposing your label function is called formatNumbers_LabelFunction"
private function formatNumbers_LabelFunction(item:Object, column:DataGridColumn):String{
//Write any code logic you want to apply on your data field ;)
//In this example, I'm using a number formatter to edit numbers
return myCustomNumberFormatter.format(item[column.dataField]);
}
This way, you can use a generic label function to handle some unified operations on your displayed data
And besides that, you can also access to any data field that is in the data provider by just calling its name like this:
item.YourFieldName
where item is the firs parameter [of type Object] in your label function method
This would work:
<DataGridColumn labelFunction="{function(item:Object, column:DataGridColumn):String { return anotherLabelFunction(item,column,2) }}" />
// Elsewhere ...
function anotherLabelFunction(item:Object,column:DataGridColumn,precision:int):String
{
// Do your business
}

Flex: Sort -- Writing a custom compareFunction?

OK, I am sorting an XMLListCollection in alphabetical order. I have one issue though. If the value is "ALL" I want it to be first in the list. In most cases this happens already but values that are numbers are being sorted before "ALL". I want "ALL" to always be the first selection in my dataProvider and then the rest alphabetical.
So I am trying to write my own sort function. Is there a way I can check if one of the values is all, and if not tell it to do the regular compare on the values?
Here is what I have:
function myCompare(a:Object, b:Object, fields:Array = null):int
{
if(String(a).toLowerCase() == 'all')
{
return -1;
}
else
if(String(b).toLowerCase() == 'all')
{
return 1;
}
// NEED to return default comparison results here?
}
//------------------------------
var sort:Sort = new Sort();
sort.compareFunction = myCompare;
Is there a solution for what I am trying to do?
The solution from John Isaacks is awesome, but he forgot about "fields" variable and his example doesn't work for more complicated objects (other than Strings)
Example:
// collection with custom objects. We want to sort them on "value" property
// var item:CustomObject = new CustomObject();
// item.name = 'Test';
// item.value = 'Simple Value';
var collection:ArrayCollection = new ArrayCollection();
var s:Sort = new Sort();
s.fields = [new SortField("value")];
s.compareFunction = myCompare;
collection.sort = s;
collection.refresh();
private function myCompare(a:Object, b:Object, fields:Array = null):int
{
if(String((a as CustomObject).value).toLowerCase() == 'all')
{
return -1;
}
else if(String((b as CustomObject).value).toLowerCase() == 'all')
{
return 1;
}
// NEED to return default comparison results here?
var s:Sort = new Sort();
s.fields = fields;
var f:Function = s.compareFunction;
return f.call(null,a,b,fields);
}
Well I tried something out, and I am really surprised it actually worked, but here is what I did.
The Sort class has a private function called internalCompare. Since it is private you cannot call it. BUT there is a getter function called compareFunction, and if no compare function is defined it returns a reference to the internalCompare function. So what I did was get this reference and then call it.
private function myCompare(a:Object, b:Object, fields:Array = null):int
{
if(String(a).toLowerCase() == 'all')
{
return -1;
}
else if(String(b).toLowerCase() == 'all')
{
return 1;
}
// NEED to return default comparison results here?
var s:Sort = new Sort();
var f:Function = s.compareFunction;
return f.call(null,a,b,fields);
}
Thanks guys, this helped a lot. In our case, we needed all empty rows (in a DataGrid) on the bottom. All non-empty rows should be sorted normally. Our row data is all dynamic Objects (converted from JSON) -- the call to ValidationHelper.hasData() simply checks if the row is empty. For some reason the fields sometimes contain the dataField String value instead of SortFields, hence the check before setting the 'fields' property:
private function compareEmptyAlwaysLast(a:Object, b:Object, fields:Array = null):int {
var result:int;
if (!ValidationHelper.hasData(a)) {
result = 1;
} else if (!ValidationHelper.hasData(b)) {
result = -1;
} else {
if (fields && fields.length > 0 && fields[0] is SortField) {
STATIC_SORT.fields = fields;
}
var f:Function = STATIC_SORT.compareFunction;
result = f.call(null,a,b,fields);
}
return result;
}
I didn't find these approaches to work for my situation, which was to alphabetize a list of Strings and then append a 'Create new...' item at the end of the list.
The way I handled things is a little inelegant, but reliable.
I sorted my ArrayCollection of Strings, called orgNameList, with an alpha sort, like so:
var alphaSort:Sort = new Sort();
alphaSort.fields = [new SortField(null, true)];
orgNameList.sort = alphaSort;
orgNameList.refresh();
Then I copied the elements of the sorted list into a new ArrayCollection, called customerDataList. The result being that the new ArrayCollection of elements are in alphabetical order, but are not under the influence of a Sort object. So, adding a new element will add it to the end of the ArrayCollection. Likewise, adding an item to a particular index in the ArrayCollection will also work as expected.
for each(var org:String in orgNameList)
{
customerDataList.addItem(org);
}
Then I just tacked on the 'Create new...' item, like this:
if(userIsAllowedToCreateNewCustomer)
{
customerDataList.addItem(CREATE_NEW);
customerDataList.refresh();
}

How to prevent tree items from being dragged into themselves?

I have a flex tree with dragMoveEnabled = true. I want to find out, when an item is dragged into itself or it's children. I'd like to use DragManager.showFeedback(DragManager.NONE) in the tree's onDragOver handler, but can't find out how get this to work. I'm using an ArrayCollection of nested objects as dataSource for the tree.
private function onDragOver(event:DragEvent):void {
event.preventDefault();
event.currentTarget.hideDropFeedback(event);
var index:int = tree.calculateDropIndex(event);
tree.selectedIndex = index;
var subCategory:CategoryVO = CategoryVO(tree.selectedItem);
var currentCategory:CategoryVO = subCategory;
while(currentCategory.parent != 0) {
if (currentCategory.parent == _draggedItem.id) {
DragManager.showFeedback(DragManager.NONE);
return;
}
currentCategory = tree.getParentItem(currentCategory);
if (currentCategory == null) {
break;
}
DragManager.showFeedback(DragManager.MOVE);
tree.showDropFeedback(event);
}
That's how I solved it.
_draggedItem holds the currently dragged item, set in the tree's onDragEnter handler. CategoryVO is the value object I use.
can you do something like:
if(event.currentTarget == event.target){
//item is being dropped on itself
}

To check if an object is empty or not

I want to check in my function if a passed argument of type object is empty or not. Sometimes it is empty but still not null thus I can not rely on null condition. Is there some property like 'length'/'size' for flex objects which I can use here.
Please help.
Thanks in advance.
If you mean if an Object has no properties:
var isEmpty:Boolean = true;
for (var n in obj) { isEmpty = false; break; }
This is some serious hack but you can use:
Object.prototype.isEmpty = function():Boolean {
for(var i in this)
if(i != "isEmpty")
return false
return true
}
var p = {};
trace(p.isEmpty()); // true
var p2 = {a:1}
trace(p2.isEmpty()); // false
You can also try:
ObjectUtil.getClassInfo(obj).properties.length > 0
The good thing about it is that getClassInfo gives you much more info about the object, eg. you get the names of all the properties in the object, which might come in handy.
If object containes some 'text' but as3 doesn't recognize it as a String, convert it to string and check if it's empty.
var checkObject:String = myObject;
if(checkObject == '')
{
trace('object is empty');
}
Depends on what your object is, or rather what you expect it to have. For example if your object is supposed to contain some property called name that you are looking for, you might do
if(objSomeItem == null || objSomeItem.name == null || objSomeItem.name.length == 0)
{
trace("object is empty");
}
or if your object is actually supposed to be something else, like an array you could do
var arySomeItems = objSomeItem as Array;
if(objSomeItem == null || arySomeItems == null || arySomeItems.length == 0)
{
trace("object is empty");
}
You could also use other ways through reflection, such as ObjectUtil.getClassInfo, then enumerate through the properties to check for set values.... this class help:
import flash.utils.describeType;
import flash.utils.getDefinitionByName;
public class ReflectionUtils
{
/** Returns an Array of All Properties of the supplied object */
public static function GetVariableNames(objItem:Object):Array
{
var xmlPropsList:XMLList = describeType(objItem)..variable;
var aryVariables:Array = new Array();
if (xmlPropsList != null)
{
for (var i:int; i < xmlPropsList.length(); i++)
{
aryVariables.push(xmlPropsList[i].#name);
}
}
return aryVariables;
}
/** Returns the Strongly Typed class of the specified library item */
public static function GetClassByName($sLinkageName:String):Class
{
var tObject:Class = getDefinitionByName($sLinkageName) as Class;
return tObject;
}
/** Constructs an instance of the speicified library item */
public static function ConstructClassByName($sLinkageName:String):Object
{
var tObject:Class = GetClassByName($sLinkageName);
//trace("Found Class: " + tMCDefinition);
var objItem:* = new tObject();
return objItem;
}
public static function DumpObject(sItemName:String, objItem:Object):void
{
trace("*********** Object Dump: " + sItemName + " ***************");
for (var sKey:String in objItem)
{
trace(" " + sKey +": " + objItem[sKey]);
}
}
//}
}
Another thing to note is you can use a simple for loop to check through an objects properties, thats what this dumpobject function is doing.
You can directly check it as follow,
var obj:Object = new Object();
if(obj == null)
{
//Do something
}
I stole this from a similar question relating to JS. It requires FP 11+ or a JSON.as library.
function isEmptyObject(obj){
return JSON.stringify(obj) === '{}';
}
can use use the hasProperty method to check for length
var i:int = myObject.hasProperty("length") ? myObject.length: 0;

Flex: Database driven DataGrid: arrows disappearing

In Flex I'm using the following code to allow sorting in a DataGrid (the data is paged and sorted serverside).
private function headerReleaseHandler(event:DataGridEvent):void
{
var column:DataGridColumn = DataGridColumn(event.currentTarget.columns[event.columnIndex]);
if(this.count>0)
{
if(this.query.SortField == column.dataField)
{
this.query.SortAscending = !this.query.SortAscending;
}
else
{
this.query.SortField = column.dataField;
this.query.SortAscending = true;
}
this.fill();
}
event.preventDefault();
}
This works perfectly, except that the arrows that indicate sorting isn't shown. How can I accomplish that?
Thanks!
/Niels
There is an example here if this is what you are looking for:
http://blog.flexexamples.com/2008/02/28/displaying-the-sort-arrow-in-a-flex-datagrid-control-without-having-to-click-a-column/
It looks like you need to refresh the collection used by your dataprovider.
I have encountered the same problem and the only solution I found was to override the DataGrid and create a custom one.
Here is the class:
public class DataGridCustomSort extends DataGrid
{
public function DataGridCustomSort()
{
super();
addEventListener(DataGridEvent.HEADER_RELEASE,
headerReleaseHandlerCustomSort,
false, EventPriority.DEFAULT_HANDLER);
}
public function headerReleaseHandlerCustomSort(event:DataGridEvent):void {
mx_internal::sortIndex = event.columnIndex;
if (mx_internal::sortDirection == null || mx_internal::sortDirection == "DESC")
mx_internal::sortDirection = "ASC";
else
mx_internal::sortDirection = "DESC";
placeSortArrow();
}
}
You have to specifically call the placeSortArrow() method when you get the HEADER_RELEASE event and set the column index and direction information.
in the above code what does "this" refer to is it the datagrid because I am confused by this.query.SortField , I am assuming 'this' and "query' are your own custom objects. and why are you checking for count. what count is that.
Regards
-Mohan

Resources