Flex DropdownList CreationComplete error - apache-flex

I have a DropdownList that shows a list of providers & the Provider associated with that Patient must be selected.
The Dropdown list:
<s:DropDownList id="providerList"
width="80%"
fontSize="12"
fontWeight="bold"
selectionColor="white"
creationComplete="providerList_creationCompleteHandler(event)"
dataProvider="{model.practiceProviderList.practiceProviders}"/>
where practiceProviders is an ArrayCollection
The CreationCompleteHandler function:
protected function providerList_creationCompleteHandler(event:FlexEvent):void
{
var firstN:String;
var lastN:String;
var providerObj:Provider = new Provider();
if (model.patientDetails.patientDetail.patientProviders != null && model.patientDetails.patientDetail.patientProviders.length > 0)
{
firstN = patientDetailsModel.patientDetails.patientDetail.patientProviders.getItemAt(0).provider.providerName.firstName;
lastN = patientDetailsModel.patientDetails.patientDetail.patientProviders.getItemAt(0).provider.providerName.lastName;
for (var count:int = 0; count < patientDetailsModel.practiceProviderList.practiceProviders.length; ++count)
{
providerObj = patientDetailsModel.practiceProviderList.practiceProviders.getItemAt(count, 0).provider as Provider;
if (providerObj.providerName.firstName == firstN && providerObj.providerName.lastName == lastN)
{
this.providerList.selectedIndex = count;
}
}
}
}
The issue is when I go to this page the first time, the error is :
TypeError: Error #1009: Cannot access a property or method of a null object reference.
at com.newwavetechnologies.modules::demographics/providerList_creationCompleteHandler()[C:\harish\flex\apps\workspace\dataCollection-flexUserInterface\src\com\newwavetechnologies\modules\demographics.mxml:166]
at com.newwavetechnologies.modules::demographics/__providerList_creationComplete()[C:\harish\flex\apps\workspace\dataCollection-flexUserInterface\src\com\newwavetechnologies\modules\demographics.mxml:359]
at flash.events::EventDispatcher/dispatchEventFunction()
at flash.events::EventDispatcher/dispatchEvent()
at mx.core::UIComponent/dispatchEvent()[E:\dev\4.0.0\frameworks\projects\framework\src\mx\core\UIComponent.as:12266]
at mx.core::UIComponent/set initialized()[E:\dev\4.0.0\frameworks\projects\framework\src\mx\core\UIComponent.as:1577]
at mx.managers::LayoutManager/doPhasedInstantiation()[E:\dev\4.0.0\frameworks\projects\framework\src\mx\managers\LayoutManager.as:759]
at mx.managers::LayoutManager/doPhasedInstantiationCallback()[E:\dev\4.0.0\frameworks\projects\framework\src\mx\managers\LayoutManager.as:1072]
where line 166 is:
if (providerObj.providerName.firstName == firstN && providerObj.providerName.lastName == lastN)
The providerObj is null the first time. But when hit back and come to the same page again, everything works fine and 1 of the providers in the list is selected correctly.
Probably I think the first time, the creationComplete handler method is called before the List is populated. The 2nd time when the call is made, the list is populated and the handler works fine. It would be great if someone can help me in this regard on how to go about this.
Thanks
Harish

It's hard to tell what's going on here, but the problem lies here:
providerObj = patientDetailsModel.practiceProviderList.practiceProviders.getItemAt(count, 0).provider as Provider;
There's a tonne of places in that line that Null pointer exceptions could occur.
Most likely - the practiceProvider returned at position count doesn't have a provider set. We can't see how this value is populated, but given this code works later, I'd say you've got a race condition happening - the data is being accessed before it's been set.
At very least, you should add a guardClause for this:
var practiceProviders:ArrayCollection = patientDetailsModel.practiceProviderList.practiceProviders;
for (var count:int = 0; count < practiceProviders.length; ++count)
{
providerObj = practiceProviders.getItemAt(count, 0).provider as Provider;
if (!providerObj)
continue;
// etc
}
The race condition is a little trickier, given the asyncronous natoure of flex server calls. (I'm assuming that you're loading the data from a remote server).
There's two approaches to solve this - either
defer execution of this method until the data has loaded - you could do this by adding an eventListener to the ResultEvent of the RemoteService
or
Don't worry about it the first time around, but re-execute the method whenever the data changes.
eg:
protected function providerList_creationCompleteHandler(event:FlexEvent):void
{
dataProvider.addEventListener(CollectionEvent.COLLECTION_CHANGE,onCollectionChange,false,0,true);
updateProviders();
// Rest of existing creationComplete code moved to updateProviders();
}
private function updateProviders()
{
// Code from existing creationComplete handler goes here
}
private function onCollectionChange(event:CollectionEvent):void
{
updateProviders();
}

Related

Multiple OnSaving event in DevExpress XAF

Im working on a piece of code using DevExpress XAF, I noticed that if im using the event OnSaving that the code executes 2 times, how can i prevent that
protected override void OnSaving()
{
if (PrestamoP != null)
{
PrestamoP.Prestado -= Monto;
PrestamoP.Save();
}
else if (PrestamoG != null)
{
PrestamoG.Prestado -= Monto;
PrestamoG.Save();
}
base.OnSaving();
}
XPO does not guarantee that the OnSaving method is called once. See the corresponding note in the XPO Best Practices article.
I can see that you are changing the PrestamoP.Prestado property based on the value of the Monto property. This code is fine if you execute it only once and only when the Monto property is specified for the first time. This code is not fine if you:
Save this object without changing the Monto property;
Update the early specified Monto value.
So, it appears that a more complex logic is required for the PrestamoG.Prestado property. First, I would move it to the Monto property setter and take the previous value into account (do not forget to check the IsLoading property in this case). Second, I would consider calculating the Prestado value dynamically instead of storing its value. This will allow you to resolve issues with the duplicate business logic execution. See an example here: How to: Calculate a Property Value Based on Values from a Detail Collection.
I can offer different methods for CRUD functions on onSaving method.
IsNewObject, IsDeleted.
// insert
if (Session.IsNewObject(this))
{
a = new a(Session);
a.CreateReferencedProperties(this);
}
// delete
else if (IsDeleted)
{
a= Session.FindObject<A>(PersistentCriteriaEvaluationBehavior.InTransaction, CriteriaOperator.Parse("A=?", this));
if (a!= null)
a.Delete();
}
// update
else
{
a= Session.FindObject<A>(PersistentCriteriaEvaluationBehavior.InTransaction, CriteriaOperator.Parse("A=?", this));
if (a!= null)
a.CreateReferencedProperties(this);
}
You can use the code below to prevent xaf from entering on saving twice.
base.OnSaving();
SessionObjectLayer sessionObjectLayer = this.Session.ObjectLayer as SessionObjectLayer;
if (sessionObjectLayer == null || sessionObjectLayer.ParentSession == null)
{
//Enter only once
}

Is it possible to find the function and/or line number that caused an error in ActionScript 3.0 without using debug mode?

I'm currently trying to implement an automated bug reporter for a Flex application, and would like to return error messages to a server along with the function/line number that caused the error. Essentially, I'm trying to get the getStackTrace() information without going into debug mode, because most users of the app aren't likely to have the debug version of flash player.
My current method is using the UncaughtErrorEvent handler to catch errors that occur within the app, but the error message only returns the type of error that has occurred, and not the location (which means it's useless). I have tried implementing getStackTrace() myself using a function name-grabber such as
private function getFunctionName (callee:Function, parent:Object):String {
for each ( var m:XML in describeType(parent)..method) {
if ( this[m.#name] == callee) return m.#name;
}
return "private function!";
}
but that will only work because of arguments.callee, and so won't go through multiple levels of function calls (it would never get above my error event listener).
So! Anyone have any ideas on how to get informative error messages through the global
error event handler?
EDIT: There seems to be some misunderstanding. I'm explicitly avoiding getStackTrace() because it returns 'null' when not in debug mode. Any solution that uses this function is what I'm specifically trying to avoid.
Just noticed the part about "I don't want to use debug." Well, that's not an option, as the non-debug version of Flash does not have any concept of a stack trace at all. Sucks, don't it?
Not relevant but still cool.
The rest is just for with the debug player.
This is part of my personal debug class (strangely enough, it is added to every single project I work on). It returns a String which represents the index in the stack passed -- class and method name. Once you have those, line number is trivial.
/**
* Returns the function name of whatever called this function (and whatever called that)...
*/
public static function getCaller( index:int = 0 ):String
{
try
{
throw new Error('pass');
}
catch (e:Error)
{
var arr:Array = String(e.getStackTrace()).split("\t");
var value:String = arr[3 + index];
// This pattern matches a standard function.
var re:RegExp = /^at (.*?)\/(.*?)\(\)/ ;
var owner:Array = re.exec(value);
try
{
var cref:Array = owner[1].split('::');
return cref[ 1 ] + "." + owner[2];
}
catch( e:Error )
{
try
{
re = /^at (.*?)\(\)/; // constructor.
owner = re.exec(value);
var tmp:Array = owner[1].split('::');
var cName:String = tmp.join('.');
return cName;
}
catch( error:Error )
{
}
}
}
return "No caller could be found.";
}
As a side note: this is not set up properly to handle an event model -- sometimes events present themselves as either not having callers or as some very weird alternate syntax.
You don't have to throw an error to get the stack trace.
var myError:Error = new Error();
var theStack:String = myError.getStackTrace();
good reference on the Error class
[EDIT]
Nope after reading my own reference getStackTrace() is only available in debug versions of the flash player.
So it looks like you are stuck with what you are doing now.

Flex Advanced Data Grid w/ hierarchical data: How to access currentTarget fields on dragdrop event?

So this is driving me crazy. I've got an advanced data grid with a dataprovider that's an array collection w/ hierarchical data. Each object (including the children) has an id field. I'm trying to drag and drop data from within the ADG. When this happens, I need to grab the id off the drop target and change the dragged object's parentid field. Here's what I've got:
public function topAccountsGrid_dragDropHandler(event:DragEvent):void{
//In this function, you need to make the move, update the field in salesforce, and refresh the salesforce data...
if(checkActivateAccountManageMode.selected == true) {
var dragObj:Array = event.dragSource.dataForFormat("treeDataGridItems") as Array;
var newParentId:String = event.currentTarget['Id'];
dragObj[0].ParentId = newParentId;
} else {
return;
}
app.wrapper.save(dragObj[0],
new mx.rpc.Responder(
function():void {
refreshData();
},
function():void{_status = "apex error!";}
)
);
}
I can access the data I'm draggin (hence changing parentId) but not the currentTarget. I think the hierarchical data is part of the problem, but I can't find much in the documentation? Any thoughts?
event.currentTarget is not a node, it's the ADG itself. However, it's quite easy to get the information you want, since the ADG stores that data internally (as mx_internal).
I'm using the following code snippets (tested with Flex SDK 4.1) in a dragOver handler, but I guess it will work in a dragDrop handler too.
protected function myGrid_dragDropHandler(event:DragEvent):void
{
// Get the dragged items. This could either be an Array, a Vector or NULL.
var draggedItems:Object = getDraggedItems(event.dragSource);
if (!draggedItems)
return;
// That's our ADG where the event handler is registered.
var dropTarget:AdvancedDataGrid = AdvancedDataGrid(event.currentTarget);
// Get the internal information about the dropTarget from the ADG.
var dropData:Object = mx_internal::dropTarget._dropData;
// In case the dataProvider is hierarchical, get the internal hierarchicalData aka rootModel.
var hierarchicalData:IHierarchicalData = dropTarget.mx_internal::_rootModel;
var targetParent:Object = null;
// If it's a hierarchical data structure and the dropData could be retrieved
// then get the parent node to which the draggedItems are going to be added.
if (hierarchicalData && dropData)
targetParent = dropData.parent;
for each (var draggedItem:Object in draggedItems)
{
// do something with the draggedItem
}
}
protected function getDraggedItems(dragSource:DragSource):Object
{
if (dragSource.hasFormat("treeDataGridItems"))
return dragSource.dataForFormat("treeDataGridItems") as Array;
if (dragSource.hasFormat("items"))
return dragSource.dataForFormat("items") as Array;
if (dragSource.hasFormat("itemsByIndex"))
return dragSource.dataForFormat("itemsByIndex") as Vector.<Object>;
return null;
}
var dropData:Object = mx_internal::dropTarget._dropData;
should be
var dropData:Object = dropTarget.mx_internal::_dropData;
Try this.

Flex Datagrid Query

Below is part of my code interacting with data grid...!
This lists the children of the particular node if i click on it after refreshing the datagrid..
But if i click on an empty space of the datagrid i get an error saying
"ReferenceError: Error #1069: Property data not found on mx.controls.listClasses.ListBaseContentHolder and there is no default value."
How to avoid that ?
if(event.target.data.children != null)
{
resultSet.removeAll();
var tempChildObj:ArrayCollection;
tempChildObj=event.target.data.children as ArrayCollection;
var childLength:int;
childLength=tempChildObj.length;
for(var i:int =0;i<childLength;i++)
{
resultSet.addItem(tempChildObj.getItemAt(i));
}
resultSet.addItem(tempChildObj);
}
}
What the error is telling you is that the event's target property doesn't have a property called data in your error case. This makes sense since you're clicking on a blank row - a blank row won't contain any data.
What you'll want to do first is check if the event's target contains a data property before you start accessing the child property of data. Either one of the following tests should work:
if( event.target.hasOwnProperty( "data" ) ) {
// rest of your code here
}
or
if( event.target.data ) {
// rest of your code here
}

Flex 3.2 casting problem

I'm newish to flex an have recently hit a snag casting.
this is the code I'm running.
/**
* return background definition depending on time
*/
private function findBackgroundItem( relativeTime : Number ) : CSBackgroundItem
{
if( this.backgroundItems == null ) return null;
var result :CSBackgroundItem = null;
var relative:Date = new Date(relativeTime);
for( var i : Number = 0; i < this.backgroundItems.length; i++ )
{
// backgroundItems is an Ilist of CSBackgroundItem.
var colourItem : CSBackgroundItem = CSBackgroundItem( this.backgroundItems.getItemAt( i ) );
// other stuff here
}
return result;
}
The problem occurs when the IList.getItemsAt() result is cast to the CSBackgroundItem variable colourItem. The following error is thrown
TypeError: Error #1034: Type Coercion failed: cannot convert com.mystuff::CSBackgroundItem#650e8dd1 to com.mystuff.CSBackgroundItem.
If I use the 'as' keyword I get cast results in the colourItem being null. Using the debugger shows that the list is not empty and is indeed populated with CSBackgroundItem objects.
Now this is the wacky bit.. this code works, the first time the module it's in loads.. subsequent loads (after unloading it) throw the exception.
Can anyone shed any light on why this might happen?
FYI, a type loaded into a child ApplicationDomain can be cast to a type (that it extends/implements) in the parent ApplicationDomain.
Eg.
loader.applicationDomain = ApplicationDomain.currentDomain; // parent domain
loader.applicationDomain = new ApplicationDomain(ApplicationDomain.currentDomain); // child domain
loader.applicationDomain = new ApplicationDomain(); // new domain
I think I may have found the answer..
My guess is the module is loading in a different ApplicationDomain.. which would mean you can't cast from the same type in the current domain..
As I understand this is vaguely similart to different Java ClassLoaders loading the same class.
I'll post again once I've confirmed it
Yup.. that works..
here is the fix i used in MXML style..
<mx:ModuleLoader id="loader" x="10" y="10" width="100%" height="100%" applicationDomain="{ApplicationDomain.currentDomain}"/>
the equivilant actionscript would be
loader.applicationDomain = ApplicationDomain.currentDomain;

Resources