Flex Datagrid labelFunction query - actionscript-3

Main.mxl
<s:DataGrid dataProvider="{employeeData}"> // employeeData is an Arraycollection with predefined data
<s:typicalItem>
<s:DataItem firstName="Christopher"
lastName="Winchester"
hireDate="22/12/2013"/>
</s:typicalItem>
<s:columns>
<s:ArrayList>
<s:GridColumn labelFunction="employeeName"
headerText="Name"/>
<s:GridColumn dataField="hireDate"
headerText="Hire Date"
labelFunction="dateFormat"/>
</s:ArrayList>
</s:columns>
</s:DataGrid>
<fx:Script>
<![CDATA[
import mx.collections.ArrayCollection;
import mx.controls.dataGridClasses.DataGridColumn;
import mx.rpc.events.ResultEvent;
[Bindable]
private var employeeData: ArrayCollection;
private function employeeName(item: Object, column: GridColumn): String
{
return item.firstName+" "+item.lastName;
}
]]>
</fx:Script>
A) Can anyone please explain me how does Datagrid internally works with employeeName function? I mean, Iam not even passing 2 parameters for labelFunction, BUT still how does it get called?
B) Why should I use s:ArrayList tag inside s:columns tag?

A) Can anyone please explain me how does Datagrid internally works
with employeeName function? I mean, Iam not even passing 2 parameters
for labelFunction, BUT still how does it get called?
The labelFunction is a property on the GridColumn class. Inside the Gridcolumn there is an itemToString() function which is used to determine what the label should be for that specific instance of the column. right out of the framework code:
/**
* #private
* Common logic for itemToLabel(), itemToDataTip(). Logically this code is
* similar to (not the same as) LabelUtil.itemToLabel().
*/
private function itemToString(item:Object, labelPath:Array, labelFunction:Function, formatter:IFormatter):String
{
if (!item)
return ERROR_TEXT;
if (labelFunction != null)
return labelFunction(item, this);
var itemString:String = null;
try
{
var itemData:Object = item;
for each (var pathElement:String in labelPath)
itemData = itemData[pathElement];
if ((itemData != null) && (labelPath.length > 0))
itemString = (formatter) ? formatter.format(itemData) : itemData.toString();
}
catch(ignored:Error)
{
}
return (itemString != null) ? itemString : ERROR_TEXT;
}
B) Why should I use s:ArrayList tag inside s:columns tag?
Because the data type of the columns property on the DataGrid is an IList; and the ArrayList implements the IList interface. What you're looking at is the MXML way to create and define an ArrayList. You'd use a slightly different approach if you wanted to create the columns in ActionScript.

To answer your first question: the "labelFunction" property is a reference to the function that will be invoked by the DataGrid to format the text of a cell in a column. The function will be called dynamically and the DataGrid will pass in the required parameters. The DataGrid expects a label function to always have this signature. If you fail to do so, you will get a runtime error.
Technically, a function can be called dynamically with the following syntax:
var anObject:MyType;
var methodName:String = "myMethod";
anObject[methodName](param1, param2);
or if you have a Function object
var myFunction:Function;
myFunction(param1, param2);

Related

Value specified in SortField not being used by ArrayCollection Sort

I saw this earlier question (Flex arraycollection sorting not working) but it doesn't seem to pertain to the issue I am seeing.
I am trying to do a sort of an ArrayCollection using a custom compare function (using the example from here: Alphanumeric Sorting in AS3 )
The problem: It seems that the field name specified in my SortField instance is not being passed to the compare function – instead the objects themselves are. This seems wrong – but maybe I am misunderstanding (this question and the answers are a little confusing: Flex: Sort -- Writing a custom compareFunction?) – what is the point of specifying a field name in the SortField if it isn't going to be used?
A stripped down example is below. I am trying to sort a list of File instances. A trace statement in the compare function confirms that the File instance, not their name properties are being passed as arguments.
Do I need to customize the compare function? That is ** cough ** less that an optimal solution for reusable coding.
Update:
As often happens, I come up with a solution withing 10 minutes of posting a question. In this case using a proxy function. Still, I am wondering why the value specified in SortField isn't being used.
public function customCompare(obj1:*, obj2:*):int
{
return AlphaNumericSort.compare(obj1.name, obj2.name);
}
<?xml version="1.0" encoding="utf-8"?>
<s:WindowedApplication xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx"
creationComplete="windowedapplication1_creationCompleteHandler(event)">
<fx:Script>
<![CDATA[
import mx.collections.ArrayCollection;
import mx.events.FlexEvent;
import spark.collections.Sort;
import spark.collections.SortField;
import utils.AlphaNumericSort;
protected function windowedapplication1_creationCompleteHandler(event:FlexEvent):void
{
var directory:File = File.desktopDirectory.resolvePath("testFolder");
if (directory.exists){
var collection:ArrayCollection;
var obj:*;
var sort:Sort;
var sortField:SortField;
collection = new ArrayCollection(directory.getDirectoryListing());
sort = new Sort();
sortField = new SortField("name");
sortField.compareFunction = AlphaNumericSort.compare;
sort.fields = [sortField];
collection.sort = sort;
collection.refresh();
trace("---------AlphaNumericSort")
for each (obj in collection){
trace(obj.name);
}
trace(" ");
}
}
]]>
</fx:Script>
</s:WindowedApplication>
I have tested your code. It works fine. Only, in your code replace the below line
sortField.compareFunction = AlphaNumericSort.compare;
to sortField.compareFunction = customCompare;

accessing current rowData of dataprovider in flex view

I have a view
<view:SampleDataGridView
rowCount="{Math.min(MAX_ROW_COUNT, hostComponent.dataList.length)}"
dataprovider="{hostComponent.dataList}"
buttonMode="true"
click="clickRow(event)"
/>
I want to get which row was clicked. I tried using currentTarget and target from event object however it wasn't of much use.
Is there a way I can pass some parameter in clickEvent function like clickEvent(rowData) or clickEvent(currentRowIndex)?
Is there any property when we use dataProvider to show currentIndex?
Data Grid
<mx:DataGrid id="employeeDataGrid" doubleClickEnabled="true" itemDoubleClick="selectEmployee(event);" dataProvider={employeeList} >
Handler
protected function selectEmployee(event:ListEvent):void
{
var selectedEmployee:Employee = event.currentTarget.selectedItem as Employee;
}

Flex getElementByName

I know that there is no such function as getElementByName in Flex but I also now that you can do this["object_id"] to get the element of the application u're in.
What about getting an element inside another element?
I've tried making element["id"] ? But in my try-catch it always runs the "catch" part =\
So: how do I get an element inside another element just having it's id in dynamically created string form?
Thank you in advance
It depends on what kind of element you are trying to access.
A child display object can be accessed by calling DisplayObjectContainer#getChildByName:
element.getChildByName("name");
A public variable (which could be set to also contain a child display object) can be accessed by using bracket syntax:
element["name"];
or simply using dot syntax:element.name
(where name is the name of the property you are trying to access).
Note that any instance you drag to the stage in the Flash IDE will automatically be assigned to a public variable, if you have the "automatically declare stage instances" option checked in your export settings. That is why using this[name]works.
If I understand correctly, you're asking for a way to get all the "elements" in a Flex application that have a certain name.
Here's an example (Flex 3):
<?xml version="1.0"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" xmlns="*">
<mx:Script>
<![CDATA[
private function testIt():void
{
var arr:Array = getDisplayObjectsByName(this, "foo");
for each (var e:* in arr)
trace(e);
}
private static function getDisplayObjectsByName(node:DisplayObjectContainer,
name:String, recurse:Boolean = true):Array
{
var n:int = node.numChildren;
var a:Array = [];
for (var i:int = 0; i < n; i++) {
var c:DisplayObject = node.getChildAt(i);
if (c.name == name)
a.push(c);
if (recurse) {
if (c is DisplayObjectContainer)
a = a.concat(getDisplayObjectsByName(DisplayObjectContainer(c),
name, true));
}
}
return a;
}
]]>
</mx:Script>
<mx:VBox name="foo">
<mx:HBox>
<mx:Button name="foo" label="Test" click="testIt()" />
</mx:HBox>
</mx:VBox>
<mx:Label text="Ignore Me" />
<mx:VBox name="bar">
</mx:VBox>
</mx:Application>
Here we're looking for all the elements called "foo" when the user clicks the "Test" button.
Output:
main0.foo
main0.foo.HBox5.foo
You'll notice that getDisplayObjectsByName() is static. All it does is traverse the display list (depth-first) and pick out all the objects with the specified name.
If you're looking for the element in a Group, you can use this function:
static function getElementByName(group:GroupBase, name:String):IVisualElement {
const child:DisplayObject = group.getChildByName(name);
const index:int = group.getChildIndex(child);
return group.getElementAt(index);
}

Strange Behaviour on DataGrid with ArrayCollection as DataProvider

I have a Datagrid with an ArrayCollection as DataProvider, the arrayCollection is partially generated by a remoteObject call, the dataprovider seems to works at least until I try to edit the field...
By the RemoteObject I only receive an ArrayCollection with the field ip, but the datagrid looks for the fields ip, check and save...
If I add/edit this new field it works, but only under particular condition
The DataGrid:
<s:DataGrid id="datagrid" left="10" right="10" top="136"
dataProvider="{listaIPCheck}" bottom="10" requestedRowCount="4">
<s:columns>
<s:ArrayList>
<s:GridColumn dataField="ip" headerText="Asset"/>
<s:GridColumn dataField="check" headerText="Inventory"/>
<s:GridColumn dataField="save" headerText="Salvataggio"/>
</s:ArrayList>
</s:columns>
</s:DataGrid>
The Script:
[Bindable]private var listaIPCheck:ArrayCollection;
private function ro_resultHandler(event:Event=null):void
{
listaIPCheck = new ArrayCollection();
listaIPCheck = ro.getListUpdate.lastResult;
heap = 0;
// Read Below {POINT #1}
init3();
}
private function init3():void
{
// Read Below {POINT #2}
if (heap<listaIPCheck.length)
{
// omitted the initialization of the process p
p.addEventListener(NativeProcessExitEvent.EXIT, onExit);
try{
p.start(startupInfo);
}catch(e:Error){}
}
}
private function onExit(e:NativeProcessExitEvent):void {
// Read below {POINT #3}
}
Here is my code, now as you can see there are 3 line where I wrote to read below...
Let's assume to put this simple for instead of the commented line (once at a time)
for (var k:Number=0;k<listaIPCheck.length;k++)
{
listaIPCheck.getItemAt(k).check = "checkVal";
listaIPCheck.getItemAt(k).save = "saveVal";
}
This code always work in the 3 points, so at the end of the call the ArrayCollection is always filled with the new values, but the datagrid refresh the items only in point #1 and #2
Why not in Point #3???
The reason the DataGrid doesn't refresh when you change properties on an item in an ArrayCollection is that because changing the properties does not trigger the collectionChange event. The DataGrid has no way to know that properties within the object changed. It has to do with pointers and memory spaces and what exactly the DataGrid is looking at for binding purposes.
In most cases, the invalidateList() method to force a refresh of the display. You can call the refresh() method or the itemUpdated() method on the collection or replace the dataProvider completely to force a refresh.

How to communicate between my model and flex component?

I'm trying to get my Actionscript 3.0 model that links to an SQLite database using Probertson's SQLRunner class to talk to my flex component; I'm really unsure of how best to accomplish this. I have worked off a few examples, but I don't know the simplest way to tell my component the results of the SQL query. Anyone have any recommendations?
Here is some of the code, to give you an idea of what I'm working with right now.
Component
<fx:Declarations>
<model:Patient id="editedPatient" FirstName="{FirstName.text}" />
</fx:Declarations>
<fx:Script>
<![CDATA[
/*imports*/
protected var _patient:Patient;
public function get patient():Patient
{
return _patient;
}
[Bindable]
public function set patient(value:Patient):void
{
_patient = value;
}
private function creationCompleteHandler(event:FlexEvent):void{
_patient.getPatient(currentUser);
}
protected function save_clickHandler(event:MouseEvent):void
{
_patient.update(editedPatient);
}
]]>
</fx:Script>
<s:TextInput id="FirstName" text="{patient.FirstName}" />
<s:Button id="save" label="save" click="save_clickHandler(event)" />
Model
public function getPatient(PatientId:int):void {
var stmt:String = new String();
stmt = "SELECT * FROM Patient WHERE PatientID= #PatiendId;";
sqlRunner.execute(stmt, {PatientId:PatientId}, loadPatient_result, Patient);
}
private function loadPatient_result(result:SQLResult):void
{
if (result.data != null && result.data.length > 0)
{
var Patient:Patient = result.data[0];
}
}
There's a few things you can do...
First, I would create a Model that follows the Singleton pattern so you can bind to any data changes in any view, or component.
Second, I would then update that singleton'd model with in the loadPatient_result method you call.
If you want to de-couple from the result and the component, you could dispatch a custom event manually that contains the patient record, have the component listen for that kind of event and update itself accordingly. Or have that view listen for that event and update accordingly really.
You're on the right track. I think Singleton is what you need.