Creating MXML components via an ActionScript loop in Flex - apache-flex

I'm using Flash Builder 4.6 to create an app for a uni project. I have a custom Spark component, and I need to dynamically create a number of instances of that component at runtime depending on the number of XML elements returned via a PHP script.
That might be a bit confusing, so let me write the steps:
1) The application sends an HTTPService request to a PHP script hosted on the server.
2) The PHP accesses the SQL database and returns a series of XML data.
3) The ActionScript dynamically creates X instances of my custom Flex component, where X is the number of data in the XML.
Here's the code I've got so far (untidy because I'm trying to make it work):
ActionScript:
[Bindable]
public var holderArray:Array = new Array(100);
public function createMenu(e:MouseEvent):void {
var count:int = 0;
var curMenuItem:menuItemContainer = new menuItemContainer();
while (count < loadedMenu.length){
curMenuItem.itemName = loadedMenu.getItemAt(count).name;
curMenuItem.itemDesc = loadedMenu.getItemAt(count).description;
curMenuItem.itemPrice = numForm.format(loadedMenu.getItemAt(count).price);
curMenuItem.imageFile = loadedMenu.getItemAt(count).url;
//curMenuItem.y = count * 120
//menuItemGroup.addElement(curMenuItem);
holderArray[count] = curMenuItem;
count ++;
}
//testString = holderArray[1].itemName;
var count2:int = 0;
for each (var menuItem:menuItemContainer in holderArray){
menuItem.name = "menuItem" + count2;
menuItem.id = "menuItem" + count2;
//testString += menuItem.name;
menuItemGroup.addElement(menuItem);
count2++;
}
}
MXML:
<s:VGroup id="menuItemGroup" x="40" y="150">
</s:VGroup>
What seems to be happening with that code is that each of my three XML data that get returned are being used in instances of menuItemContainer, but when each one is added to menuItemGroup, it's overwriting the one that's already there. I'm not sure if the item is actually getting overwritten, or if the new item is just sitting atop the earlier ones, but if the latter is true I can't find a way to arrange the components. I've tried setting menuItem.y in the loop (as a function of count2), but to no avail.
Thanks in advance for any and all suggestions/answers.
Benjamin.

Put this line
var curMenuItem:menuItemContainer = new menuItemContainer();
inside your while loop. With your code as it is you are only creating one instance of menuItemContainer then continually changing the properties of that one menuItemContainer in your while loop. Instead you need to create a new, different instance of menuItemContainer with each iteration of the loop.

Related

Process data from service in flex

I have created a PHP service in Flex Mobile and binded the result with a List. I want to sort the records from the service based on some calculation on data fields. So, is it possible to perform operations on the resultset from the PHP service ?
Detailed question:
I have created the php service through the built in tool in Flash Builder 4.6. I also had bind the service with a List in my view. (Again using GUI controls). Now I want to rearrange the items in the list based on distance from the current location and coordinates from the rows returned by the service. So the approach I was thinking was to, get the content from the service. Extract lat,lon and calculate the distance. Then update the list again.
I tried the following line in a function:
getAllplacesResult.lastResult[1].lon // lon is a column in the table.
But it returns a blank. Is their a more sane or easier way to do this ?
The better way is to assign it on an Arraycollection and manipulate data from there.
<mx:ArrayCollection id="myAC" source="{ArrayUtil.toArray(myRO.getAllplacesResult.lastResult)}" />
Make sure you bind myAC to your List.
Sort it the way you want to:
var collection:ArrayCollection = new ArrayCollection();
var s:Sort = new Sort();
s.fields = [new SortField("lat"), new SortField("lon")];
s.compareFunction = myCompareFunction;
collection.sort = s;
collection.refresh();
private function myCompareFunction(a:Object, b:Object, fields:Array = null):int {
...
}
Also, AS3 is 0 based index. 1 will actually give you the second row (or throw out of range exception)

Ninject/.NET MVC3 - Retriving instances of my object

Here's some code,
k.Bind<IGame>().To<Game>().Named("A")
.WithConstructorArgument("ColorChoiceCount", 12);
iGame = k.Get<IGame>("A");
((Game)iGame).SelectedColor = new GameColor(System.Drawing.Color.Red);
iGame = k.Get<IGame>("A");
On the first iGame = k.Get<IGame>("A"); I get a new instance of Game.
Next line: I change one of it's properties.
Next line (iGame = k.Get<IGame>("A"); again) I get a new instance again.
What I would like is to be able to retrieve instances I've already used.
But I'm totally new to this kind of tools so I guess I'm missing something.
Thank you if you can help me.
You need to specify the lifetime of your object - by default the container will create a new instance.
The available methods are as follows:
InScope
InTransientScope
InThreadScope
InSingletonScope
InRequestScope
http://blog.bobcravens.com/2010/03/ninject-life-cycle-management-or-scoping/
You probably want a singleton (single instance of the game):
k.Bind<IGame>().To<Game>().InSingletonScope().Named("A")
.WithConstructorArgument("ColorChoiceCount", 12);

Factory Method implementation in actionscript

Hey folks, i ve got this issue implementing the Factory method.
Following is the snippet of the the main chart class which calls ChartFactory's method to attain the proper object. I Type Cast chartobject so as to be able to call the Show method;i m apprehensive about that as well.
container = new VBox();
container.percentWidth = 100;
container.percentHeight = 100;
super.media.addChild(container);
chartObject = new ChartBase();
chartObject = ChartFactory.CreateChartObject(chartType);
IChart(chartObject).Show(o);
container.addChild(chartObject);
legend = new Legend();
legend.dataProvider = IChart(chartObject);
container.addChild(legend);
Following is the snippet of ChartFactory's method:
public static function CreateChartObject(subType:String):ChartBase
{
switch(subType)
{
case ChartFactory.AREA_CHART:
return new AreaCharts();
break;
case ChartFactory.COLUMN_CHART:
return new ColumnCharts();
break;
case ChartFactory.PIE_CHART:
return new PieCharts();
break;
default:
throw new ArgumentError(subType + ": Chart type is not recognized.");
}
}
And following is Show method of one of the several Charts type classes: AreaCharts, PieCharts etc. All of which implements IChart Interface.
public function Show(o:ObjectProxy):void
{
var grids:GridLines;
var stroke:SolidColorStroke;
var horizontalAxis:CategoryAxis;
var verticalAxis:LinearAxis;
var horizontalAxisRenderer:AxisRenderer;
var verticalAxisRenderer:AxisRenderer;
grids = new GridLines();
if(WidgetStylesheet.instance.LineChart_ShowGrid)
grids.setStyle("gridDirection", "both");
else
grids.setStyle("gridDirection", "");
stroke = new SolidColorStroke(WidgetStylesheet.instance.LineChart_GridLineColor, WidgetStylesheet.instance.LineChart_GridLineThickness);
grids.setStyle("horizontalStroke", stroke);
grids.setStyle("verticalStroke", stroke);
horizontalAxis = new CategoryAxis();
horizontalAxis.categoryField = o.LargeUrl.Chart.xField;
horizontalAxis.title = o.LargeUrl.Chart.xAxisTitle.toString();
verticalAxis = new LinearAxis();
verticalAxis.title = o.LargeUrl.Chart.yAxisTitle.toString();
horizontalAxisRenderer = new AxisRenderer();
horizontalAxisRenderer.axis = horizontalAxis;
horizontalAxisRenderer.setStyle("tickLength", 0);
horizontalAxisRenderer.setStyle("showLine", false);
horizontalAxisRenderer.setStyle("showLabels", true);
horizontalAxisRenderer.setStyle("fontSize", WidgetStylesheet.instance.ComputeChartAxisFontSize(o.HeadlineFontSize));
verticalAxisRenderer = new AxisRenderer();
verticalAxisRenderer.axis = verticalAxis;
verticalAxisRenderer.setStyle("tickLength", 0);
verticalAxisRenderer.setStyle("showLine", false);
verticalAxisRenderer.setStyle("fontSize", WidgetStylesheet.instance.ComputeChartAxisFontSize(o.HeadlineFontSize));
this.series = this.m_createSeries(o);
this.horizontalAxis = horizontalAxis;
this.horizontalAxisRenderers = [horizontalAxisRenderer];
this.verticalAxis = verticalAxis;
this.verticalAxisRenderers = [verticalAxisRenderer];
this.backgroundElements = [grids];
}
I'm afraid that there is more than one issue with this code. Unfortunately it is not obvious why your chart doesn't show up so you may apply some of advices below and use debugger to analyse the issue.
There is no point in creating ChartBase instance if you are going to change value of chartObject reference in the next line
chartObject = new ChartBase();
chartObject = ChartFactory.CreateChartObject(chartType);
If the API of your charts is IChart your factory should return IChart instead of casting.
public static function CreateChartObject(subType:String):IChart
Make sure that you are returning instances of the correct class from the factory. i.e. that you are returning your subclass of standard PieChart. Generally it's not the best idea to extend the class keeping the same name and just changing the package.
Once again, if you are not sure if the program enters some function use the Flash Builder debugger to check this. I can't imagine development without debugger.
Some thoughts:
you call the Show method, pass it some object but nowhere in that method is any child added to a displayObject. What exactly is Show supposed to do?
a lot of member variables in your classes start with UpperCase. The compiler can easily confuse those with class names, in case your classes are named the same. Bad practice to start variable and function names with capitals.
If your casting an instance to another class or interface fails, you will get a runtime error. Those are easy to debug using the Flash Builder debugger.
Hey ppl..
i found out wat wnt wrng..as olwys it wa "I".
I ve a habit of mkin mock ups secluded from the main project n dn integrate it. So in mock up i hd used an xml whch hd a format slightly diff dn d one being used in the main project.
N i hd a conditional chk to return from the prog if certain value doesnt match, n due to faulty xml i did'nt.
So this more a lexical error than a logical one.
Sorry n Thanx evryone for responding.

Is there a API from Flex/Flash to list all the UIComponent?

I am trying to create an API utility using Adobe AIR where given a component name it should list all it methods, variables, and other properties of that component.
But I could not find an API to list all the UIComponents of Flex/Flash.
is there an API to lookup all the UIComponent or if there is no such API, can anyone tell me how do i lookup the components?
Note: I have tried using flash.utils.describeType() but it is not able to suit my requirement as the input to the method is passed dynamically using TextInput.
~Jegan
It sounds like you are asking two things:
How to list all of the properties/methods of a UIComponent.
How to list all of the UIComponents in the Flex SDK.
The first one can be done with describeType(). Given that the method is passed dynamically using TextInput, you need to just include all of the UIComponents into the SWF. You can do that with the following include statement for each component in the SDK (this example is with the Button):
include mx.controls.Button; Button;.
Then you can do something like this:
var className:String = myTextArea.text; // mx.controls::Button or mx.controls.Button
var clazz:Class = flash.utils.getDefinitionByName(className);
var methods:Array = getMethods(clazz);
var properties:Array = getProperties(clazz); // write a similar method for accessors and variables
/**
* Returns a list of methods for the class.
* Pass in the superclass depth for how many classes
* you want this to return (class tree). If it's -1, it will return the whole method list
*/
public static function getMethods(target:Object, superclassDepth:int = -1, keepGeneratedMethods:Boolean = false):Array
{
var description:XML = DescribeTypeCache.describeType(target).typeDescription;
var hierarchy:Array = getHierarchy(target);
var methodList:XMLList = description..method;
methodList += description.factory..method; // factory is required if target is Class
var methodName:String
var methods:Array = [];
var declaredBy:String;
var methodXML:XML;
var i:int = 0;
var n:int = methodList.length();
for (i; i < n; i++)
{
methodXML = methodList[i];
declaredBy = methodXML.#declaredBy;
methodName = methodXML.#name;
// we break because the xml list is orded hierarchically by class!
if (superclassDepth != -1 && hierarchy.indexOf(declaredBy) >= superclassDepth)
break;
// ignore methods that start with underscore:
if (methodName.charAt(0) == "_" && !keepGeneratedMethods)
continue;
methods.push(methodName);
}
// sort the method list, so there's some kind of order to the report:
methods.sort(Array.CASEINSENSITIVE);
return methods;
}
The second one, listing the UIComponents in the SDK, is a bit more complex because there's no stable list of components in the SDK (depends on version, etc.). What you can do, however, is get a list of every class included in the swf. You do this by generating a Link Report (which is used for helping out modules), which is just a compiler argument:
-link-report=my_report.xml
Then you can figure out a way to find the unique xml nodes in that file, and which ones extend UIComponent. I use ruby and the nokogiri xml parser for ruby to do that. There is also a neat AIR app to visualize the Link Report.
If you're doing the first case, that method should get you started on listing all properties/methods of a UIComponent. If you're doing the second, that will give you a list of all UIComponents included in the swf.
Let me know if that helps,
Lance
Do you need to do anything with the data, other than displaying it? If not, it might be easiest to pull up the right page from Adobe's online API docs.

Why am I losing my databinding when printing to an XpsDocument?

Update!
Binding works. The issue is that the XpsDocumentWriter doesn't properly write the first page of the first document of a FixedDocumentSequence. This seems to be an issue encountered by lots of people doing this sort of thing (i.e., five developers worldwide). The solution is slightly odd. I include it as an answer.
Okay, its a bit more subtle than the question suggests.
I've got a series of FixedPages, each has its DataContext set individually. Each FixedPage also has one or more controls that are bound to the context.
If I add these FixedPages to a single FixedDocument and write this single FixedDocument to an XpsDocument, my binds are de-referenced (so to speak) and the correct values are presented in the XpsDocument.
If I add these FixedPages to individual FixedDocuments (each FP gets added to a new FD), then these FixedDocuments are added to a FixedDocumentSequence, and this sequence is then written to the XpsDocument, my binds are NOT de-referenced and my FixedPages appear blank.
Debugging tells me that I'm not losing my bindings or my binding context, so that's not the cause of this failure.
Here's some sample code to illustrate what's going on:
// This works
FixedPage fp = CreateFixedPageWithBinding();
fp.DataContext = CreateDataContext();
// Add my databound fixed page to a new fixed document
var fd = new FixedDocument();
var pc = new PageContent();
((IAddChild)pc).AddChild(fp);
fd.Pages.Add(pageContent);
// Create an xps document and write my fixed document to it
var p = Package.Open("c:\\output.xps", FileMode.CreateNew);
var doc = new XpsDocument(p);
var writer = XpsDocument.CreateXpsDocumentWriter(doc);
wri2.Write(fd);
p.Flush();
p.Close();
// This does NOT work
FixedPage fp = CreateFixedPageWithBinding();
fp.DataContext = CreateDataContext();
// Add my databound fixed page to a new fixed document
var fd = new FixedDocument();
var pc = new PageContent();
((IAddChild)pc).AddChild(fp);
fd.Pages.Add(pageContent);
// Create a fixed document sequence and add the fixed document to it
FixedDocumentSequence fds = CreateFixedDocumentSequence();
var dr = new DocumentReference();
dr.BeginInit();
dr.SetDocument(fd);
dr.EndInit();
(fds as IAddChild).AddChild(dr);
// Create an xps document and write the fixed document sequence to it
var p = Package.Open("c:\\output.xps", FileMode.CreateNew);
var doc = new XpsDocument(p);
var writer = XpsDocument.CreateXpsDocumentWriter(doc);
wri2.Write(fds);
p.Flush();
p.Close();
You can see that the only difference between the two is that I'm adding the fixed document to a fixed document sequence, which then gets written.
Obviously, whatever magic happens that causes the databinding to be evaluated and the bound values be inserted isn't happening when my fixed documents aren't being Written to the Xps Document. I need to be able to write more than one fixed document, and the Write method can only be called once, thus requiring I add the FixedDocuments to a FixedDocumentSequence which I then write. But I also need my damn databinding to work as well!
Any help in this situation would be appreciated. I know its not exactly the most common part of the framework; I'm just hoping that someone here has some operational experience with this (I'm looking at you, lurking MS employee).
The cause of this bug is that the FixedPage's layout isn't being updated prior to writing. This causes the first FixedPage in the first FixedDocument in the FixedDocumentSequence to be written incorrectly. This affects NO OTHER PAGES IN THE RESULTING DOCUMENT, which made this bug/edge case harder to nail down.
The following WORKS (rewritten version of the non-working example):
FixedPage fp = CreateFixedPageWithBinding();
fp.DataContext = CreateDataContext();
var fd = new FixedDocument();
/* PAY ATTENTION HERE */
// set the page size on our fixed document
fd.DocumentPaginator.PageSize =
new System.Windows.Size()
{
Width = DotsPerInch * PageWidth,
Height = DotsPerInch * PageHeight
};
// Update the layout of our FixedPage
var size = fd.DocumentPaginator.PageSize;
page.Measure(size);
page.Arrange(new Rect(new Point(), size));
page.UpdateLayout();
/* STOP PAYING ATTENTION HERE */
var pc = new PageContent();
((IAddChild)pc).AddChild(fp);
fd.Pages.Add(pageContent);
// Create a fixed document sequence and add the fixed document to it
FixedDocumentSequence fds = CreateFixedDocumentSequence();
var dr = new DocumentReference();
dr.BeginInit();
dr.SetDocument(fd);
dr.EndInit();
(fds as IAddChild).AddChild(dr);
// Create an xps document and write the fixed document sequence to it
var p = Package.Open("c:\\output.xps", FileMode.CreateNew);
var doc = new XpsDocument(p);
var writer = XpsDocument.CreateXpsDocumentWriter(doc);
wri2.Write(fds);
p.Flush();
p.Close();
I found this issue while trying to use XpsDocumentWriter to write to a PrintQueue. The following code prints the first page correctly.
//Prints correctly
FixedDocumentSequence Documents = new FixedDocumentSequence();
//some code to add DocumentReferences to FixedDocumentSequence
PrintDialog printDialog = new PrintDialog
{
PrintQueue = LocalPrintServer.GetDefaultPrintQueue()
};
printDialog.PrintTicket = printDialog.PrintQueue.DefaultPrintTicket;
if (printDialog.ShowDialog() == true)
{
Documents.PrintTicket = printDialog.PrintTicket;
XpsDocumentWriter writer = PrintQueue.CreateXpsDocumentWriter(printDialog.PrintQueue);
writer.Write(Documents, printDialog.PrintTicket);
printerName = printDialog.PrintQueue.FullName;
}
If you remove the printDialog.ShowDialog() and just attempt to silent print to the default printer, the first page prints incorrectly. However, in my scenario, I didn't need to use a FixedDocumentSequence so I swapped it out for just a single FixedDocument and silent printing worked. I tried updating the layout on the FixedPage without success. Weird how the first page prints fine if I show the print dialog though.
One reason that you lose a binding is that you throw an exception somewhere - unfortunately, this exception is silently swallowed and your binding just "stops working". Turn on First-chance exceptions and see if anything gets hit.

Resources