How to read the Dynamic form Child data in Flex? - apache-flex

i created a form dynamically by adding each component in action script,
now i want to get back the text/data entered in to that each component dynamically?
private function loadAllComponents():void
{
var formItemArray:Array = new Array();
for(var i:int=0; i< Application.application.designList.length; i++)//which had the colonName, colComponet to be dispalyed,
{
var fm:FormItem = new FormItem();
fm.label = Application.application.designList.getItemAt(i).colName;
var comp:String = Application.application.designList.getItemAt(i).component;
switch(comp)
{
case "TextBox":
var ti:TextInput = new TextInput();
ti.id = Application.application.designList.getItemAt(i).component;
fm.addChild(ti);
break;
case "TextArea":
var ta:TextArea = new TextArea();
ta.id = Application.application.designList.getItemAt(i).colName;
fm.addChild(ta);
break;
case "ComboBox":
var mycb:myComboBox = new myComboBox();
mycb.getAllMasterCBData(Application.application.selectedgridItem, Application.application.designList.getItemAt(i).colName);
fm.addChild(mycb);
break;
case "DateField":
var df:DateField = new DateField();
df.id = Application.application.designList.getItemAt(i).component;
fm.addChild(df);
break;
}
myform.addChild(fm);
}
}
private function saveToDb():void // Here i wan to read all the formdata
{
var formItems:Array = myform.getChildren();
for each (var item:UIComponent in formItems)
{
if (item is TextInput)
{
var text:String = Object(item).text;
Alert.show("came here");
}
else if (item is DateField)
{
var date:Date = DateField(item).selectedDate;
}
}
}
]]>
</mx:Script>
<mx:Form id="myform" cornerRadius="5" borderColor="#B7BABC" borderStyle="solid" width="100%" height="100%" />
<mx:HBox width="100%" height="100%" >
<mx:Spacer width="120"/>
<mx:Button label=" Save " id="saveBtn" click="saveToDb()" />
</mx:HBox>

You're creating the input components in ActionScript, but based on this code you are not creating them dynamically; you're just hard coding them. With your given sample, you'll know the components you are creating at compile time.
You'll need to store a reference to the form items you create; make them public variables instead of 'var' local variables. Kind of like this:
protected var ti:TextInput ;
protected var ta:TextArea ;
protected var df:DateField;
Then in your creation method, do something like this:
ti = new TextInput();
ti.id = Application.application.designList.getItemAt(i).component;
fm.addChild(ti);
ta = new TextArea();
ta.id = Application.application.designList.getItemAt(i).colName;
fm.addChild(ta);
df = new DateField();
df.id = Application.application.designList.getItemAt(i).component;
fm.addChild(df);
myform.addChild(fm);
Then when you need to access them, just do something like this:
private function getMyformData()
{
ti.text;
ta.text;
}
If you're generating the form components at run time based on data, then store then form elements in an array of some sort.
You could also work something out by looping over all children of your container, although that wouldn't be my first approach.
Since poster posted more complete code; here are some additions. I added the protected array of all form items and in each 'switch' block; the new input element is pushed onto the array.
<mx:Script>
protected var itemsArray : Array = new Array();
private function loadAllComponents():void
{
var formItemArray:Array = new Array();
for(var i:int=0; i< Application.application.designList.length; i++)//which had the colonName, colComponet to be dispalyed,
{
var fm:FormItem = new FormItem();
fm.label = Application.application.designList.getItemAt(i).colName;
var comp:String = Application.application.designList.getItemAt(i).component;
switch(comp)
{
case "TextBox":
var ti:TextInput = new TextInput();
ti.id = Application.application.designList.getItemAt(i).component;
fm.addChild(ti);
itemsArray.push(ti)
break;
case "TextArea":
var ta:TextArea = new TextArea();
ta.id = Application.application.designList.getItemAt(i).colName;
fm.addChild(ta);
itemsArray.push(ta)
break;
case "ComboBox":
var mycb:myComboBox = new myComboBox();
mycb.getAllMasterCBData(Application.application.selectedgridItem, Application.application.designList.getItemAt(i).colName);
fm.addChild(mycb);
itemsArray.push(mycb)
break;
case "DateField":
var df:DateField = new DateField();
df.id = Application.application.designList.getItemAt(i).component;
fm.addChild(df);
itemsArray.push(df)
break;
}
myform.addChild(fm);
}
}
The sateToDb method will change to be something like this:
private function saveToDb():void // Here i wan to read all the formdata
{
var formItems:Array = myform.getChildren();
for each (var item:UIComponent in itemsArray )
{
if (item is TextInput)
{
var text:String = Object(item).text;
Alert.show("came here");
}
else if (item is DateField)
{
var date:Date = DateField(item).selectedDate;
}
}
}
]]>
</mx:Script>

Edited Response:
OK, I think I see the issue.
You're adding your data controls to FormItems and adding those to the Form. But then you're iterating over the Form's children and as if they were the data controls and not FormItems.
Without commenting on the rest of the code, have a look at what this updated function is doing to retrieve the data controls:
private function saveToDb():void
{
var formItems:Array = myform.getChildren();
for each (var item:FormItem in formItems)
{
var itemChildren:Array = item.getChildren();
for each (var control:UIComponent in itemChildren)
{
if (control is TextInput)
{
var text:String = Object(item).text;
Alert.show("TextInput");
}
else if (control is DateField)
{
var date:Date = DateField(item).selectedDate;
Alert.show("Date");
}
}
}
You can delete the formItemArray variable too, it's not needed since we're getting the list of children from the Form and FormItems.
Original response:
If you keep a reference to each of the dynamic form items in an Array you can iterate over each of them in your getMyFormData() function.
e.g.
protected var formItems:Array = new Array();
// Other class stuff here...
var ti:TextInput = new TextInput();
ti.id = Application.application.designList.getItemAt(i).component;
formItems.push(ti); // Add item to array.
fm.addChild(ti);
var ta:TextArea = new TextArea();
ta.id = Application.application.designList.getItemAt(i).colName;
formItems.push(ta); // Add item to array.
fm.addChild(ta);
var df:DateField = new DateField();
df.id = Application.application.designList.getItemAt(i).component;
formItems.push(df); // Add item to array.
fm.addChild(df);
myform.addChild(fm);
<mx:button click="getMyformData()"/>
private function getMyformData()
{
//How to get the myform Data dynamically here after validations... ? &
for each (var item:UIComponent in formItems)
{
if (item is TextInput || item is TextArea)
{
// Cast to Object to access the 'text' property without the compiler complaining.
var text:String = Object(item).text;
// Do something with the text...
}
else if (item is DateField)
{
var date:Date = DateField(item).selectedDate;
// Do something with the date...
}
// Insert additional type checks as needed.
}
}
You'll have to work out what to do with the data on your own though :)
If you are using a separate list make sure you clear out the formItems array when you're done with it so you don't have references to the items keeping them in memory unnecessarily.
Instead of keeping a separate array of form items you could also iterate over the children in the fm container. You might have to make some assumptions about the children you'd be accessing but it looks like you have control over all of the children being added so that's no problem.
I hope that helps...
:)

Related

Looping through an arraylist sourced from an XML file

I am reading in an XML file that is shown in the attached image. I'm reading it in using URLRequest, which works properly. The next thing I'd like to do is to populate an arraylist with all of the "project" nodes. I'm converting the XML to an array, but the source is showing the project as being in the [0] node and the arraylist's length is 1.
What's the proper way to do this so I can loop through all the projects in the arraylist?
private var xmlParameters:XML
private var xmlStoryMap:XMLList;
protected function application1_creationCompleteHandler(event:FlexEvent):void
{
var params:Object;
var xmlLoader:URLLoader = new URLLoader();
xmlLoader.addEventListener(Event.COMPLETE, xmlloader_onComplete_Handler);
xmlLoader.addEventListener(IOErrorEvent.IO_ERROR,IOError_handler);
xmlLoader.load(new URLRequest("myXML.xml"));
}
protected function xmlloader_onComplete_Handler(event:Event):void
{
var loader:URLLoader = URLLoader(event.target)
xmlParameters = new XML(loader.data);
xmlStoryMap = xmlParameters.projects;
initializeMap();
}
protected function initializeMap():void
{
var testlist:ArrayList = new ArrayList();
testlist.source = convertXMLtoArray(xmlStoryMap.project);
}
private function convertXMLtoArray(file:String):Array
{
var xml:XMLDocument = new XMLDocument(file);
var decoder:SimpleXMLDecoder = new SimpleXMLDecoder;
var data:Object = decoder.decodeXML(xml);
var array:Array = ArrayUtil.toArray(data);
return array;
}
If you don't want to have a loop issue, use this instead
protected function xmlloader_onComplete_Handler(event:Event):void
{
var loader:URLLoader = URLLoader(event.target)
var xmlString:String = loader.data;
initializeMap(xmlString);
}
protected function initializeMap(xmlString:String):void
{
var testlist:ArrayList = new ArrayList();
testlist.source = convertXMLtoArray(xmlString);
}
private function convertXMLtoArray(xmlString:String):Array
{
var xmlDoc:XMLDocument = new XMLDocument(xmlString);
var decoder:SimpleXMLDecoder = new SimpleXMLDecoder();
var data:Object = decoder.decodeXML(xmlDoc);
return ArrayUtil.toArray(data.storymap.projects.project);
}
For looping through the projects,
for each(var projectXML:XML in xmlParameters.projects.project)
{
// Do operation
}

Flex sort array collection by inner class

I want to sort an array collection in a way that I am not sure is possible.
Usually when you want to sort you have something like this.
var dataSortField1:SortField = new SortField();
dataSortField1.name = fieldOneToSortBy;
dataSortField1.numeric = fieldOneIsNumeric;
var dataSort:Sort = new Sort();
dataSort.fields = [dataSortField1];
arrayCollection.sort = dataSort;
arrayCollection.refresh();
so if I had a class
public class ToSort {
public var int:a
}
I could type
var ts1:ToSort = new ToSort();
ts1.a = 10;
var ts2:ToSort = new ToSort();
ts2.a = 20;
var arrayCollection:ArrayCollection = new ArrayCollection([ts1, ts2])
var dataSortField1:SortField = new SortField();
dataSortField1.name = "a";
dataSortField1.numeric = true;
var dataSort:Sort = new Sort();
dataSort.fields = [dataSortField1];
arrayCollection.sort = dataSort;
arrayCollection.refresh();
This works fine. My problem is I now have inherited a class that has another class inside it and I need to sort against this as well.
For example
public class ToSort2 {
public var int:a
public var ToSortInner: inner1
}
public class ToSortInner {
public var int:aa
public var int:bb
}
if a is the same in multiple classes then I want to sort on ToSortInner2.aa Is this possible. I have tried to pass in inner1.aa as the sort field name but this does not work.
Hope this is clear. If not I'll post some more code.
Thanks
You need to write a custom sort compareFunction. You can drill down into the objects properties inside the function.
Conceptually something like this:
public function aSort(a:Object, b:Object, fields:Array = null):int{
if(a.aa is b.aa){
return 0
} else if(a.aa > b.aa) {
return 1
} else{
return -1
}
}
When you create your sort object, you can specify the compare function:
var dataSort:Sort = new Sort();
dataSort.compareFunction = aSort;
arrayCollection.sort = dataSort;
arrayCollection.refresh();
Sort also has another property, compareFunction.

Flex 3: Setting one arrayCollection to another is stalling the application

I'll just go ahead and C/P the entire function to ensure you guys see everything going on:
public function directorsPrepsToShow():void
{
var tempDPrepsAC:ArrayCollection = new ArrayCollection;
var dprepSD:Date = new Date;
var dprepED:Date = new Date;
var viewSD:Date = rightDate(startViewDate.getMonth(), startViewDate.getDate(), startViewDate.getFullYear());
var viewED:Date = rightDate(viewSD.getMonth(), viewSD.getDate() + 14, viewSD.getFullYear());
var newACIDs:String = new String;
var useACIDs:String = new String;
for each (var item:Object in dPrepAC)
{
dprepSD = textToDate(item[2]);
dprepED = rightDate(dprepSD.getMonth(), Number(dprepSD.getDate() + (item[3] - 1)), dprepSD.getFullYear());
if (dateCollider(dprepSD, dprepED, viewSD, viewED))
tempDPrepsAC.addItem(item as Array);
}
if (tempDPrepsAC.length != usePrepAC.length)
{
usePrepAC = new ArrayCollection();
usePrepAC = tempDPrepsAC;
Alert.show("HI");
}
}
This function is in a separate file, that's called from the main mxml via the following:
<mx:Script source="functions/dprep.as" />
The line that's causing the app to stall is "usePrepAC = tempDPrepAC;". usePrepAC is declared in the main mxml like this:
[Bindable] public var usePrepAC:ArrayCollection = new ArrayCollection;
Dose anybody see why this one line would cause the application to stall? If I comment out that line, the application loads fine (loads everything except for the information that this AC should contain). I've been looking at this now for about an hour, trying different ways to get the contents of tempDPrepsAC into usePrepAC - but nothing is working. I tried googling it, but found nothing :(
Thanks,
Brds
EDIT
dprep AC is declared in the main mxml like this:
[Bindable] public var dPrepAC:ArrayCollection = new ArrayCollection;
And the function that populates it is as follows:
public function createDirectorsPrepCollection(e:ResultEvent):void
{
var xmlList:XMLList = XML(e.result).directorsprep;
var dupString:String = "|";
var tempArray:Array = new Array;
for (var i:int = 0; i < xmlList.length(); i++)
{
if (dupString.indexOf(String("|" + xmlList[i].name.#id) + "|") == -1)
{
tempArray = new Array;
tempArray[0] = xmlList[i].prepDBID;
tempArray[1] = xmlList[i].projectDBID;
tempArray[2] = xmlList[i].startdate;
tempArray[3] = xmlList[i].numdays;
tempArray[4] = xmlList[i].positions;
dPrepAC.addItem(tempArray);
dupString += "|" + xmlList[i].prepDBID + "|";
}
}
directorsPrepsToShow();
}
This function is called by this:
<mx:HTTPService id="dprepHttp" url="{dprepXML}" resultFormat="e4x" makeObjectsBindable="true" result="createDirectorsPrepCollection(event)" />
dPrepAC is populating fine btw... I check it in a for each loop.
Try using following code:
usePrepAC.source = tempDPrepsAC.source;

load symbol of flash in flex at runtime

Hey people, I've this huge problem loading a symbol from a swf file in the application at runtime. I want to load it and pass it as a argument to another class where it could be used further. The symbol name is passed on from the array collection from the "o" object. Can anybody please tell me what's the right way to go about it.. Thanks in advance..!!
Following is the code for reference..
public override function Show(o:ObjectProxy):void
{
var _this:Weather;
var _super:ContentItem;
var item:WeatherItem;
var items:ArrayCollection;
var widgetCount:Number;
var headlineFontSize:int;
var conditionsIconThemeLoader:Loader;
this.m_weatherWidgetContainer = new HBox();
super.Show(o);
_this = this;
_super = super;
(undefined == o["HeadlineFontSize"]) ? headlineFontSize = 20 : headlineFontSize = o["HeadlineFontSize"];
if (undefined != o["direction"])
this.m_textDirection = o["direction"];
if (o.LargeUrl.Forecast is ArrayCollection)
items = ArrayCollection(o.LargeUrl.Forecast);
else
items = new ArrayCollection([o.LargeUrl.Forecast]);
widgetCount = this.m_computeWidgetSpace(items.length);
conditionsIconThemeLoader = new Loader();
conditionsIconThemeLoader.contentLoaderInfo.addEventListener(Event.COMPLETE, function(e:Event):void
{
for(var i:uint = 0; i < widgetCount; i++)
{
var symbolClass:Class = e.currentTarget.loader.contentLoaderInfo.applicationDomain.currentDomain.getDefinition(int(items[i].condition)) as Class;
var symbolInstance:Sprite = new symbolClass();
item = new WeatherItem();
item.Show(items[i], headlineFontSize, symbolInstance, widgetCount);
_this.m_weatherWidgetContainer.addChild(item);
}
});
conditionsIconThemeLoader.contentLoaderInfo.addEventListener(IOErrorEvent.IO_ERROR, function(e:IOErrorEvent):void
{
Alert.show("Failure loading " + WidgetStylesheet.instance.Weather_Widget_Theme + ".swf");
});
// Attempt to load theme weather icon file
conditionsIconThemeLoader.load(new URLRequest("assets/animation/" + WidgetStylesheet.instance.Weather_Widget_Theme + ".swf"));
super.media.addChild(this.m_weatherWidgetContainer);
}
Heres the answer
public override function Show(o:ObjectProxy):void
{
var _this:Weather;
var _super:ContentItem;
var conditionsIconThemeLoader:Loader;
var loaderContext:LoaderContext;
this.m_weatherWidgetContainer = new HBox();
this.m_weatherWidgetContainer.percentHeight = 100;
this.m_weatherWidgetContainer.percentWidth = 100;
super.Show(o);
_this = this;
(undefined == o["HeadlineFontSize"]) ? this.m_headlineFontSize = 20 : this.m_headlineFontSize = o["HeadlineFontSize"];
if (undefined != o["direction"])
this.m_textDirection = o["direction"];
if (o.LargeUrl.Forecast is ArrayCollection)
this.m_items = o.LargeUrl.Forecast;
else
this.m_items = new ArrayCollection([o.LargeUrl.Forecast]);
conditionsIconThemeLoader = new Loader();
loaderContext = new LoaderContext(false, ApplicationDomain.currentDomain);
conditionsIconThemeLoader.contentLoaderInfo.addEventListener(Event.COMPLETE, this.m_loaderSuccess);
conditionsIconThemeLoader.contentLoaderInfo.addEventListener(IOErrorEvent.IO_ERROR, this.m_loaderFail);
// Attempt to load theme weather icon file
conditionsIconThemeLoader.load(new URLRequest("assets/animation/" + WidgetStylesheet.instance.Weather_Widget_Theme + ".swf"), loaderContext);
this.m_weatherWidgetContainer.addEventListener(FlexEvent.CREATION_COMPLETE, this.m_drawHorizontalLine);
super.media.addChild(this.m_weatherWidgetContainer);
}

Flex AdvancedDataGrid - ColumnOrder With Formatter and ItemRenderer Question For Experts

I have a advanceddatagrid that has around 15 columns. Some are string,
some are numbers. I have shown 4 columns below.
The number columns have formatting done for zero precision and 2
digits precision. The itemRenderer is just to show Blue Color if the
number is +ve and Red Color if the number is -ve.
It looks something like below
<mx:columns>
<mx:AdvancedDataGridColumn dataField="Name" textAlign"left"/>
<mx:AdvancedDataGridColumn dataField="Time" textAlign="right"/>
<mx:AdvancedDataGridColumn dataField="Score" textAlign="right" formatter="{zeroPrecisionFormatter}" sortable="true" itemRenderer="ColorRenderer" />
<mx:AdvancedDataGridColumn dataField="Average" textAlign="right" headerWordWrap="true" formatter="{twoPrecisionFormatter}" itemRenderer="ColorRenderer" />
...
I am trying to save the users setting of column order when he closes
the application and reload with the same order when the user opens the
applications. I am using SharedObjects and below is the code.
for(var i:int=0; i< adgrid.columns.length;i++){
var columnObject:Object = new Object();
columnObject.columnDataField = adgrid.columns[i].dataField as String;
columnObject.columnHeader =adgrid.columns[i].headerText as String;
columnObject.width = adgrid.columns[i].width;
columnArray.push(columnObject);
}
and then I save the columnArray to SharedObject.
I retrive them using the below code
for(var i:int=0;i<columnArray.length;i++){
adgrid.columns[i].dataField =columnArray[i].columnDataField;
adgrid.columns[i].headerText =columnArray[i].columnHeader;
adgrid.columns[i].width = columnArray[i].width;
}
How can I save and reload the Formatter and ItemRenderer data .
I am having trouble saving the formatter and itemrenderer and
reloading it again.
I would really appreciate if you can shown the code.
How can I reshuffle the columns but can preserve all the properties of it to though sharedobject and recover it again.
private function loadLayout(name:String="custom"):void {
var storedLayoutData:SharedObject;
storedLayoutData = SharedObject.getLocal("layouts");
if (storedLayoutData.data["pocketBankDG_"+name]) {
var columns:Array = new Array();
var index:int = 0;
for each (var column:Object in storedLayoutData.data["pocketBankDG_"+name]["columns"]) {
for ( var key:String in column) {
var propClass:Class = getDefinitionByName(column[key].propClassName) as Class;
adgOperations.columns[index][key] = column[key].propValue as propClass;
}
index++
}
} else {
saveLayout("default");
openSettingsWindow();
}
}
private function saveLayout(name:String="custom"):void {
var storedLayoutData:SharedObject;
storedLayoutData = SharedObject.getLocal("layouts");
if (!storedLayoutData.data["pocketBankDG"]) storedLayoutData.data["pocketBankDG_"+name] = new Object();
var columns:Array = new Array();
for each(var column:AdvancedDataGridColumn in adgOperations.columns) {
var describeType:XML = flash.utils.describeType(column);
var accessoriesList:XMLList = describeType..accessor;
var data:Object = new Object;
for each(var accessor:XML in accessoriesList) {
if (accessor.#access=="readwrite") {
var propClassName:String = getQualifiedClassName(column[accessor.#name]);
var propValue:* = column[accessor.#name];
if (propClassName=="String" || propClassName=="int" || propClassName=="Boolean" ||
propClassName=="Number" || propClassName=="uint") {
data[accessor.#name] = {}
data[accessor.#name].propClassName = propClassName;
data[accessor.#name].propValue = propValue;
}
}
}
columns.push(data);
}
storedLayoutData.data["pocketBankDG_"+name]["columns"] = columns;
storedLayoutData.flush();
}

Resources