Flex: Passing object to save back to server freezes application - apache-flex

I have a NavigatorContent which is displayed when the user selects an item in a DataGrid.
This NavigatorContent contains a form and an accordion displaying the related objects.
When the user presses the Save button in the NavigatorContent the form and the children should be saved to the database by calling the server through BlazeDS:
saveObjectToDB()
{
//Map the form values to the object
object.field1 = object_field1.text;
object.field2 = object_field2.selectedDate as Date;
object.relatedobject3 = comboBox.selectedItem as RelatedObject3;
//etc.....
//Loop through accordion to save the child objects
for(var i:int= 0; i < accordion.numChildren; i++ )
{
if(accordion.getChild(i) is RelatedObject1Form)
{
var formRelated1:RelatedObject1Form = accordion.getChild(i) as RelatedObject1Form;
//Map the form values to the related object
object.relatedobject1.field1 = formRelated1.relatedobject1_field1.selectedDate;
//etc...
}
if(accordion.getChild(i) is RelatedObject2Grid)
{
var gridRelated2:RelatedObject2Grid = accordion.getChild(i) as RelatedObject2Grid;
//Get dataProvider for the datagrid of the relatedObject
object.relatedobject2 = gridRelated2.object.relatedobject2;
}
}
// Call the remoting object's saveObject method
var saveObjectOperation:Operation = new Operation();
saveObjectOperation.name = "saveObject";
saveObjectOperation.arguments=[object];
ro.operations = [saveObjectOperation];
saveObjectOperation.send();
if(isNewObject)
//dispatchEvent new object
else
//dispatchEvent object updated
}
My problem is as the question states that my application freezes for a few seconds when the user presses the save button that calls this method. I guess that is because Flex is single threaded, but still i dont quite get why this method would be so computational expensive? It doesnt seem to matter if i comment out the loop that loops over the accordion children.
I tried setting the objects related objects to null before calling the remote save method, and this seemed to speed up the save method, but it provided me with some troubles later.
My conclusion is that the remote call is whats freezing up the application, and if i set the related objects to null this seems to fix the issue. But is this really necessary? The related objects aren't really that big, so i don't quite get why the remote call should freeze the application for a few seconds.
This is how i create the accordion children when the NavaigatorContent is intialized:
var relatedObjectForm:RelatedObject1Form= new RelatedObject1Form();
accordion.addChild(relatedObjectForm);
relatedObjectForm.object= object;
relatedObjectForm.ro = this.ro;
The object that i pass to the accordion children is public and [Bindable] in the NavigatorContent and in the accordion children and is initially passed from the main DataGrid. May this be a problem relating to this issue?
Any help/comments is much appreciated. This issue is starting to affect my beauty sleep ;)

My guess would be that you're spending a lot of time in the serializer. Put a trace target in the app and watch the console when it runs to see what's being sent.
The most likely problems are from DisplayObjects - if they've been added to the application they will have a reference to the application itself, and will cause some serializers to start serializing the entire app. The bindable object might have some odd events attached that eventually attach to DisplayObjects - try copying the relevant values in it into your object instead of just taking a reference to the existing object.

Related

How to pass data from one component to another component in flex

I have one class named as EmployeeResult where I am getting the response from the service. Inside the resulthandler I am getting an array of employees like name, id, age etc. I have one dataGrid inside the employeeView.mxml file. Inside the employeeView.mxml file I have an ArrayCollection which is the dataprovider to the datagrid. I want to update that arraycollection from inside the EmployeeResult file. When working with Cairngorm framework I have used the arraycollection inside the singleton to achieve the goal. In case of mate framework I have used the propertyinjector tags. But how do I achieve this objective in my case without any framework. How to achieve property injection without using ane framework or singleton class.
Continuing on your previous question: How to listen to events inside the child component dispatched by the parent component, you can simply dispatch a custom event containing that list of employees and notify the entire application of its arrival.
Something like this:
private function handleMyEmployeeResults(event:ResultEvent):void {
var employees:IList = EmployeeResult(event.result).employeeList;
dispatchEvent(new EmployeeEvent(EmployeeEvent.LIST_LOADED, employees, true));
}
Since this is a service result handler, we may assume that its class instance is not a view and hence it is not on the display list, which is why the event can't bubble. To address this we can dispatch the event directly on the stage.
FlexGlobals.topLevelApplication.stage.dispatchEvent(
new EmployeeEvent(EmployeeEvent.LIST_LOADED, employees)
);
Any view in your application can now listen for this event and set its properties accordingly:
//inside View1
stage.addEventListener(EmployeeEvent.LIST_LOADED, handleEmployeesLoaded);
private function handleEmployeesLoaded(event:EmployeeEvent):void {
myDataGrid.dataProvider = event.employees;
}
//inside View2
stage.addEventListener(EmployeeEvent.LIST_LOADED, handleEmployeesLoaded);
private function handleEmployeesLoaded(event:EmployeeEvent):void {
myOtherKindOfList.dataProvider = event.employees;
myFirstEmployeeLabel.text =
event.employees[0].firstname + event.employees[0].lastname;
}
Another more straightforward approach is to use your Application as a singleton. Create a bindable property employeeList on your main application. Now set its value when the results come in:
private function handleMyEmployeeResults(event:ResultEvent):void {
var employees:IList = EmployeeResult(event.result).employeeList;
FlexGlobals.topLevelApplication.employeeList = employees;
}
Now you can bind to this property from anywhere in your application.
<View1>
<s:DataGrid dataProvider="{FlexGlobals.topLevelApplication.employeeList}" />
</View1>
<View2>
<s:List dataProvider="{FlexGlobals.topLevelApplication.employeeList}" />
</View2>
Though this approach has the merit of being very easy to implement, it has all the downsides of a Singleton (e.g. poorly testable).
Given the types of questions you've been asking, you really should be considering a Framework such as Robotlegs or Mate. They give you the tools to wire your application together without horrible hacks that will limit your flexibility or complicate maintenance long-term.
Check out my previous answer here for links to the same project done without a framework, with Mate, and with Robotlegs.

Spark memory leaks

I've been trying to track down memory leaks in our application, and keep finding myself back looking at Spark components as the culprit.
I think I've found the cause, but my understanding of Garbage Collection / mark & sweep is not too hot, so I'd like to verify my findings.
Many classes in Spark use RichEditableText for displaying their text properties (ComboBox,TextInput).
RichEditableText has a local textContainerManager property, and frequently calls compose() on this.
Here's the relevant abridged extract from TextContainerManager
// Line 282 - 292:
static private var stringFactoryDictionary:Dictionary = new Dictionary(true);
static private function inputManagerStringFactory(config:IConfiguration):StringTextLineFactory
{
var factory:StringTextLineFactory = stringFactoryDictionary[config];
if (factory == null)
{
factory = new StringTextLineFactory(config);
stringFactoryDictionary[config] = factory;
}
return factory;
}
// Line 1204:
public function compose() {
// Line 1238:
var inputManagerFactory:TextLineFactoryBase = (_sourceState == SOURCE_STRING) ? inputManagerStringFactory(_config) : _inputManagerTextFlowFactory;
// Line 1242:
inputManagerFactory.swfContext = Configuration.playerEnablesArgoFeatures ? this : _swfContext;
}
Line 1242 is the crucial line here, as it gives the static dictionary a reference to our component.
(Note - I've checked this with the debugger to confirm which branch of the ternary gets executed.) This would prevent the instance from ever being garbage collected.
Eg: Static dictionary has a value with a reference to the instance -- instance cannot be GC'd.
In turn, this would prevent any other instances which have a reference to the instance of TextContainerManager from being GC'd also.
While this theory certainly matches what I'm seeing in our app, I can't beleive that there really is a memory leak in such a low-level spark component.
Could someone please shed some light on this?
BTW - I've opened a defect on bugs.adobe.com to track this issue, should it prove to be a genuine bug:
https://bugs.adobe.com/jira/browse/SDK-29531
I've heard there are several memory problems related to TLF.
That should be corrected in the Flex 4.5 SDK.
In the meantime, you could still open a ticket on http://bugs.adobe.com/jira/

FLEX: dialog not display immediately

In an AIR application I have the following code:
theDialog = PopUpManager.createPopUp( this, TheDialogClass, true ) as TheDialogClass;
theDialog.addEventListener(FlexEvent.CREATION_COMPLETE, cpuIntensiveCalc);
At the end of cpuIntensiveCalc the dialog is removed. The dialog informs the user that "something is going on, please stand by."
The problem is that cpuIntensiveCalc starts before the dialog draws. So the user experience is that the application freezes for 10 seconds with no indicator, then the modal dialog flashes quickly (less than a second) on screen.
The Adobe docs say this about creation_complete
Dispatched when the component has finished its construction,
property processing, measuring, layout, and drawing.
So this feels like the correct event.
In the name of completeness, I also tried
theDialog = PopUpManager.createPopUp( this, TheDialogClass, true ) as TheDialogClass;
cpuIntensiveCalc();
But had the same results.
TIA
The reason for this is that the Flash Player is single threaded, and so you are blocking the UI from reacting to the Dialog Popup until the maths chunk is finished.
Hacky fix time...
You have two options.
(This one should work, but is untested) Wrap the cpuIntensiveCalc() call in a callLater, so that the UI can finish rendering before you block the rendering.
Or
Use "Green Threads" to break up your processing so that you don't completely block the UI processing. Take a look.
(I just had the same issue => even if this thread is old, I just wanted to contribute my solution)
(disclaimer: this is a bit ugly, but they say that's ok in the UI layer... ;-) )
Flex is single threaded (at least from our developer's perspective, I think behind the scene threads are used by the VM)
=> you typically execute your code in the UI thread, after the user did some action on a widget. Any call to update a UI component (like setProgress or setLabel) will only be rendered on screen at the end of the render cycle (see again UiComponent life cycle).
=> In therory calling "cpuIntensiveCalc" in a callLater will let the framework display your popup before executing the method.
In practice though, I noticed you typically have to have for a couple of UI cylces before the popup be displayed, like this:
new MuchLaterRunner(popup, 7, cpuIntensiveCalc).runLater();
MuchLaterRunner being defined like this:
public class MuchLaterRunner
{
var uiComp:UIComponent;
var currentCounter = 0;
var cyclesToWaitBeforeExecution=0;
var command:Function;
public function MuchLaterRunner(uiComp:UIComponent, cyclesToWaitBeforeExecution:uint, command:Function)
{
this.uiComp = uiComp;
this.command = command;
this.cyclesToWaitBeforeExecution =cyclesToWaitBeforeExecution;
}
public function runLater() {
currentCounter ++;
if (currentCounter >= cyclesToWaitBeforeExecution) {
uiComp.callLater(command);
} else {
// wait one more cycle...
uiComp.callLater(runLater);
}
}
}
The issue is the same when calling setProgress afterward: we must divide cpuIntensiveCalc into small callable methods that can be ran at each UI cycle, otherwise the progressbar won't, err, progress.
Use enterFrame event on popup. Don't forget to remove the listener in the enterFrame event handler - otherwise the cpu intensive method will be called in each frame, crashing your app. If this doesn't work at first, use a private number as a counter and keep incrementing it in the enter frame handler - call cpu heavy method only when the counter reaches the appropriate value. Find the 'appropriate' value by trail and error.
theDialog = PopUpManager.createPopUp(this, TheDialogClass, true) as TheDialogClass;
theDialog.addEventListener(Event.ENTER_FRAME, onEnterFrame);
private function onEnterFrame(e:Event):void
{
//can use theDialog instead of e.currentTarget here.
(e.currentTarget).removeEventListener(Event.ENTER_FRAME, onEnterFrame);
cpuIntensiveCalc();
}
//in case the above method doesn't work, do it the following way:
theDialog.addEventListener(Event.ENTER_FRAME, onEnterFrame);
private var _frameCounter:Number = 0;
private function onEnterFrame(e:Event):void
{
_frameCounter++;
var desiredCount:Number = 1;//start with one - increment until it works.
if(_frameCounter < desiredCount)
return;
//can use theDialog instead of e.currentTarget here.
(e.currentTarget).removeEventListener(Event.ENTER_FRAME, onEnterFrame);
cpuIntensiveCalc();
}

Why is a child property getting overwritten when a Linq object is being updated?

I'm updating one object, and trying to update any child objects along with it.
Basically I'm handling the OnUpdating event of a LinqDataSource.
In the DataContext class I have the ObjectUpdate function (where right now I've just got a breakpoint so I can see the values...)
In the LinqDataSource.OnUpdating event e.NewObject.Child is null, which makes no sense whatsoever. I set that to a new value, but by the time I get to DataContext.ObjectUpdate NewObject.Child has been overwritten with the OLD value...
So somewhere between LinqDataSource.Updating and DataContext.UpdateObject it's populating the object with the old values... but I need the new ones.
Is there a way to fix that, or am I going to have a nervous breakdown?
I think I figured out the problem. After running LinqDataSource through .NET Reflector I noticed that:
1) It's the LinkDataSourceUpdateEventArguments.OriginalObject which is actually attached to the data context
2) values are copied from the NewObject into OriginalObject after OriginalObject is attached to the data context
What I don't understand is why the association properties are not copied. Maybe for the same reasons you can't serialize them?
The workaround is/was to handle the Updating event myself and do the actual submit instead of letting LinqDataSource handle that part.
void FormDataSource_Updating(object sender, LinqDataSourceUpdateEventArgs e)
{
var newObj = e.NewObject;
var table = FormContext.GetTable(e.NewObject.GetType());
if (BuildingObject != null)
BuildingObject(sender, new HeirarchicalBuildObjectEventArgs(newObj));
table.Attach(newObj, e.OriginalObject);
FormContext.SubmitChanges();
e.Cancel = true;
}

flex 3 and autoComplete

im trying to getting auto complete working and i can do so fine when i just create an array in my mxml and then just initialize an arrayCollection at the top of the file in the initialize keyword.
However i want to populate the arraycollection from a webservice but i cant seem to get it;
im my application tag i have the following
creationComplete="init()"
initialize="data2 = new ArrayCollection(data1);"
then in my init method;
private function init():void
{
userRequest.loadWSDL(wsdlUrl);
userRequest.getAllCountries();
}
//this is called when i get a result from userRequest.getAllCountries();
private function getAllCountriesResult(e:ResultEvent):void
{
data1 = new Array(e.result);
}
however my text box is not getting any value.
Anyone with ideas?
first off, Array is not Bindable so changing the variable data1 will have no knock on effect.
The arrayCollection is bindable.
So presumming that the result (e.result) is actually an array (you should check this when debugging) then you could do the following
[Bindable]
priavte var ac : ArrayCollection;
then inside you're getAllCountriesResult function.
ac = new ArrayCollection(e.result);
then anything that has is dataprovider set to the var ac will be updated.
If you wish to update a text value inside a textArea or similar then you should listen for the change event in the arrayCollection and take the appropriate action then.
from your additional points below (just edit your original question)
I take it the autocomplete your talking about is the autocomplete text input box from adobe exchange area as a normal text box doesn’t take an arrayCollection.
If you posted some code it may make it easier to help you.
Preinitialize, then initialize, then creationComplete, then applicationComplete (this is the order they get called in).
If your using the component I’m thinking of, check out http://www.websector.de/blog/2008/04/30/quick-tip-avoid-issues-using-adobes-autocomplete-input-component-using-flex-3/
It appears it may have some issues with flex 3, so check out http://blogs.adobe.com/flex/2006/09/component_autocomplete_text_in.html .
Try this:
private function getAllCountriesResult(e:ResultEvent):void
{
data2.source = new Array(e.result); // or data2.source = e.result as Array
}
Make sure data2 is already initialized as a ArrayCollection.
As for AutoComplete, I'm trying to work things out myself.

Resources