Flex getElementByName - actionscript-3

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

Related

Flex Datagrid labelFunction query

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

Create ViewStack in Actionscript with creationPolicy = "auto"

In MXML, when I add components to ViewStack and creationPolicy is auto, components are not instantiated until I switch to them. Say, I have the following code:
<mx:ViewStack creationPolicy="auto">
<s:NavigatorContent>
<s:DataGrid id="dg1" width="300"/>
</s:NavigatorContent>
<s:NavigatorContent>
<s:DataGrid id="dg2" width="100"/>
</s:NavigatorContent>
</mx:ViewStack>
How do I replicate this behavior in ActionScript?
The problem is that my DataGrids hold large chunks of data, and thus I don't want them to be created at the same time.
Looks like the ViewStack class has a creationPolicy property on it. Something like this should work
var v:ViewStack = new ViewStack();
v.creationPolicy = "auto";
var t1:NavigatorContent = new NavigatorContent();
t1.addElement(new DataGrid());
v.addElement(t1);
Please try with this one
var v:ViewStack = new ViewStack();
v.creationPolicy = "auto";
var container:SkinnableContainer = new SkinnableContainer();
container.creationPolicy = "none";
var dg:DataGrid = new DataGrid();
container.addElement(dg);
var t1:NavigatorContent = new NavigatorContent();
t1.addElement(container);
v.addElement(t1);
After viewstack_change handler we need to call
container.createDeferredContent() based on which index view you want to see.
For more details Using the createDeferredContent() method

how to assign actionscript variable value to spark component in flex

I have a variable in actionscript. How can I set the label text with the variable value? I have the following code:
public function setObjVal(obj1:InvPrintRRTObj):void
{
obj = obj1;
var date:String = obj.receive_Date;
var yy:String = date.substring(0,3);
var mm:String = date.substring(5,6);
var dd:String = date.substring(8,9);
}
I want to assign the yy value to a spark label. Please help.
The mxml code goes here
s:Label width="35" height="25" textDecoration="none" verticalAlign="middle" text="{yy}"
sorry, i was not able to format this mxml code
Another way to achieve this is to access the label by id.
<s:Label
id="myLabel"
width="35"
height="25"
textDecoration="none"
verticalAlign="middle" />
Then in your function
public function setObjVal(obj1:InvPrintRRTObj):void
{
...
myLabel.text = yy;
}
So, the problem is that the label does not have access to the yy variable, since it is defined in the setObjVal method. There are two ways to fix this:
make the yy variable global, i.e. define it outside of the method so the label component can access it
add an id to the label (e.g. 'myLabel') and add a line to the setObjMethod which updates the label's text, like so:
myLabel.text = yy; //in this case, the label should be accessible to the method

Question on bindable variables in AS3

I'm probably misusing bindable variables here, so please bear with me here while I try and explain what I'm trying to to.
I have a simple spark list where I allow people to select a background by clicking on one of the items. When a background is selected, I then save it to the SharedObject in order to use it when the user loads the application again later.
This list is populated by an ArrayCollection (binded variable) created as follows:
[Bindable] private var arrBG:ArrayCollection = new ArrayCollection();
This then gets populated the following way:
var objImage:Object;
var strSharedObjImage:String = sharedObj.sharedBackground.data.backgroundIndex;
// Background
objImage = new Object();
objImage.icon = ICONS_PATH + objImage.label;
objImage.label = "Titanium";
objImage.selected = (strSharedObjImage == objImage.fileName) ? true : false;
arrBG.addItem(objImage);
objImage = new Object();
objImage.icon = ICONS_PATH + objImage.fileName;
objImage.label = "Iron";
objImage.selected = (strSharedObjImage == objImage.label) ? true : false;
arrBG.addItem(objImage);
I then use it as the dataProvider on my spark list.
If you notice above, on my object I have a property called selected, which will get set to true, if the value of my shared object is the same as the value on the "label" property.
On the item renderer for my spark list, I have the following:
<s:ItemRenderer name="HorizontalListSkin"
xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
autoDrawBackground="false"
creationComplete="initMenuSkin(event)"
>
<fx:Script>
<![CDATA[
protected function initMenuSkin(event:Event):void
{
iconImage.source = data.icon;
iconText.text = data.label;
// Check to see if the item we're displying is selected. If it is make it stand out
if(data.selected){
iconText.setStyle("color", "Green")
}
}
]]>
</fx:Script>
<s:VGroup x="10" y="10" width="50" height="50" buttonMode="true" horizontalAlign="center">
<s:Image id="iconImage" horizontalCenter="0"/>
<s:Label id="iconText" fontFamily="Verdana" fontSize="11" fontWeight="bold" horizontalCenter="0" showTruncationTip="false"/>
</s:VGroup>
</s:ItemRenderer>
So as you can see, I'm simply changing the colour of the font on my selected item.
When I load it up, I can see that the item I have previously selected is marked in green, and if I select a new item, I would like it to now be marked as green instead.
Obviously there's a big gap in here, since nowhere in my explanation above I mention updating my bindable variable so in theory it wold propagate to my spark list (being it a bindable variable I would of thought it would simultaneously update the item on my list(?)).
Well, I have tried doing it in a few different ways, and the debugger does "say" my array has been updated, however, my list isn't being updated at all, and will only bring another item marked in green if I close the screen and open again (when it all gets reloaded)
The whole logic described above to create a new background is contained within a function, so whenever I select an item from my list of backgrounds I was triggering my "loadBackgrounds" method again, which would apply all the logic to know which is the selected background, and because the variable is binded with my spark list I'd have hoped would update the list. Thing is, it doesn't.
What am I doing wrong here? Am I going totally bonkers and there's a much easier way of doing this but only I can't see it?
Any help here would be appreciated.
Thanks in advance
After you set the data ion the collection you need to refresh it.
arrBG.refresh();
[EDIT]
Ok I re-read your question.
I think I misunderstood what you were asking.
You want to know how to update the list so the item renderer will re-render the new list after you made changes to the data provider?
function newSelection( val:String ):void{
for each( var item:Object in arrBG ){
if( item.label == val ){
item.selected = true;
}else{
item.selected = false;
}
}
arrBG.refresh();
}
// use the commit properties on your renderer not the init
// commit properties will trigger whenever there is a dataprovider update/change
<s:ItemRenderer name="HorizontalListSkin"
xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
autoDrawBackground="false"
>
<fx:Script>
<![CDATA[
override protected function commitProperties():void{
super.commitProperties();
iconImage.source = data.icon;
iconText.text = data.label;
// Check to see if the item we're displying is selected. If it is make it stand out
if(data.selected){
iconText.setStyle("color", "Green")
}
}
]]>
</fx:Script>
<s:VGroup x="10" y="10" width="50" height="50" buttonMode="true" horizontalAlign="center">
<s:Image id="iconImage" horizontalCenter="0"/>
<s:Label id="iconText" fontFamily="Verdana" fontSize="11" fontWeight="bold" horizontalCenter="0" showTruncationTip="false"/>
</s:VGroup>
</s:ItemRenderer>

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.