Update spark List control on dataProvider change - actionscript-3

I am using spark List control which is bind to an instance of such class:
[Event(name="collectionChange", type="mx.events.CollectionEvent")]
public class HierarchicalCollectionListAdapter extends EventDispatcher implements IList
{
...
}
I want to make List fully re-draw each time when this collection send "reset" collectionChange event. Now to achieve this, each time after collection update I have to call this code:
var _itemRenderer:IFactory = _list.itemRenderer;
_list.itemRenderer = null;
_list.itemRenderer = _itemRenderer;
Is there any way to do it in more elegant way?

It's a bit hard for me to see whats going on here but it looks like your list dataProvider is not extending ListCollectionView so you can't use refresh()
However, you may be able to force a redraw using invalidateDisplayList()
I would try something like this:
_list.dataGroup.invalidateDisplayList();

I'll leave my solution here in case if someone will have same problem.
Best thing that I could do - create extended list control, which will refresh itself each time on collection update. So, create new control, based on spark list and add this code there:
<fx:Script>
<![CDATA[
import mx.collections.IList;
import mx.events.CollectionEvent;
import mx.events.CollectionEventKind;
/**
* Subscribe to collection change event.
*/
override public function set dataProvider(value:IList):void
{
if (dataProvider)
dataProvider.removeEventListener(CollectionEvent.COLLECTION_CHANGE,
dataProvider_collectionChangeHandler);
if (value)
value.addEventListener(CollectionEvent.COLLECTION_CHANGE, _collectionChangeHandler, false, 0, true);
var _itemRenderer:IFactory = this.itemRenderer;
this.itemRenderer = null;
this.itemRenderer = _itemRenderer;
super.dataProvider = value;
}
/**
* If collection has changed - redraw list by resetting itemRenderer.
*/
private function _collectionChangeHandler(event:Event):void
{
if (event is CollectionEvent)
{
var ce:CollectionEvent = CollectionEvent(event);
// We don't need to refresh if any collection element will change.
if(ce.kind != CollectionEventKind.UPDATE)
{
var _itemRenderer:IFactory = this.itemRenderer;
this.itemRenderer = null;
this.itemRenderer = _itemRenderer;
}
}
}
]]>
</fx:Script>

Related

Flex: Object property changes not updating AdvancedDataGrid

I'm using an AdvancedDataGrid to display hierarchical data. The DataProvider is an object and the children field is an ArrayCollection of objects. When I update one of the objects in the ArrayCollection I'd like the grid to refresh.
After researching, I do understand that this won't happen automatically. Not even if I use itemUpdated() and then refresh the AC. Not even if I call executeBindings(recursive).
I know that to be smart I should've used ObjectProxy all along as this would solve all my problems. My issue now is how do I go about fixing this without totally revamping my Hierarchical Data objects?
I tried having my Value Objects extend ObjectProxy but that didn't do the trick. Is there an elegant solution? Can typed objects even take advantage of ObjectProxy class?
Thanks for any helpful tips.
Here is the HierarchicalData code:
<mx:HierarchicalData
source="{model.myObject}"
childrenField="trades"/>
Here is 'myObject' class:
[Bindable]
public class MyObject implements ValueObject
{
public var qty:Number;
public var status:String;
public var comments:String;
public var modified_by:String;
public var modified_date:Date;
public var trades:ArrayCollection = new ArrayCollection();
}
The 'trades' ArrayCollection holds a bunch of Trade objects defined here:
[Bindable]
public class Trade implements ValueObject
{
public var selected:Boolean = false;
public var quantity:Number;
public var modified_by:String;
public var modified_date:Date;
}
And here is how I populate the collection after the server returns data:
for ( var i:int=0;i<result.length;i++ ){
var item:Object = result[i];
var recall:MyObject = new MyObject();
recall.comments = item.comments;
recall.qty = item.qty;
recall.status = item.status;
recall.modified_by = item.modified_by;
recall.modified_date = item.modified_date;
recall.trades.addItem( item.trades );
model.recalls.addItem( recall );
}
You just need to use a bindable ArrayCollection as dataProvider for your AdvancedDataGrid. You can look at an example here
I had this issue just a week ago. I finally found a solution (this is in my initialisationComplete handler of my application):
// lets create the model
model = new ArrayCollection();
testPlanHierarchy = new HierarchicalData();
// lets create the bindings
BindingUtils.bindProperty(testPlanHierarchy,"source",this,"model");
BindingUtils.bindProperty(testPlanADG,"dataProvider",this,"testPlanHierarchy")
The testPlanADG is my AdvancedDataGrid of course and my model is an ArrayCollection of typed Objects.
The elements with [Bindable] tag are:
the model
the typed objects classes

AIR dispatch event with parameter

I have a form with a search field.
When user press enter key, I use httpservice to send a query to mySQL database.
In some case (a lot) there are several record, so a new window is opening to show those record with a datagrid to let user chose the good result.
My problem is how to send selected information to the first window (with text field).
I gess that dispatch event is the way but I don't found how to use!
Can you help me to find a solution.
Thanks
If you are trying to communicate within an MDI environment I suggest that you use some kind of shared model ( aka Mediator or Presentation Model ) that keeps a contract between the desired windows.
class SelectionPM{
[Bindable]
public var selectedItem:Object;
}
Use case:
Window1 has an instance of SelectionPM, when you open Window2 you pass
SelectionPM instance to it, then update SelectionPM.selectedItem
property on changing selection in the Window2 datagrid. That will
propagate the binding chain up to Window1, where you can use the
SelectionPM.selectedItem as you wish.
Ideally you would use an IoC container for model injection but that is another story.
That might seem like a lot of work but if you keep this methodology across your app you will benefit from it.
Cheers!
Here's a set of four classes as a basis. Obviously you don't want to be doing the actual work in the constructors as below.
public class App
{
public static var eventDispatcher:EventDispatcher = new EventDispatcher();
public function App()
{
new Window1();
}
}
class AppEvent extends Event
{
public static const DATA_READY:String = "APPEVENT.DATA_READY";
public var data:Object;
public function AppEvent( type:String, data:Object )
{
super( type );
this.data = data;
}
}
class Window1
{
public function Window1()
{
App.eventDispatcher.addEventListener( AppEvent.DATA_READY, onDataReady );
...DO STUFF...
new Window2();
}
private function onDataReady( evt:AppEvent ) : void
{
...DO STUFF WITH "evt.data"....
}
}
class Window2
{
public function Window2()
{
...GET DATA FROM SERVER AND PUT IT IN "data"...
App.eventDispatcher.dispatchEvent( new AppEvent( AppEvent.DATA_READY, data ) );
}
}
</pre>

How do I call a method when a public property changes in Flex 3?

If I have a .mxml file that has a method in it and a public property, can I have the method execute whenever the property changes.
<?xml version="1.0" encoding="utf-8"?>
<mx:HBox xmlns:mx="http://www.adobe.com/2006/mxml">
<mx:Script>
<![CDATA[
[Bindable]public var myProperty:MyType;
private function myMethod(myProperty):void
{
// Method to run every time myProperty changes
}
]]>
</mx:Script>
</mx:HBox>
In another .mxml file I have added this .mxml file like so:
<viewComponents:MyViewComponent myProperty="{myVariable}" />
Since no one said this yet, I'll propose a second approach.
Every property in the Flex Framework will dispatch a property named something like *property*Changed. where property is the name of the property to be changed. Said properties are implemented using get set methods as others have mentioned. Something like this:
private var _myProperty:MyType;
[Bindable(myPropertyChanged)]
public function get myProperty():MyType
{
return _myProperty;
}
public function set myProperty(value:MyType):void
{
_myProperty = value;
dispatchEvent(new Event('myPropertyChanged'));
}
These event name specified in the Bindable metadata is used for binding purposes. So, instead of calling your method inside the set, you could listen for this myPropertyChanged event:
component.addEventListener('myPropertyChanged',onMyPropertyChanged)
And elsewhere in the code:
protected function onMyPropertyChanged(event:Event):void{
// do other processing
}
This may be overkill for what you're trying to accomplish; or not. Since you didn't go into specifics on what you were trying to accomplish, I'm not sure.
If your new functionality relates to the Flex Component LifeCycle in some manner, such as changing the display or the size you should be performing your changes in the lifecycle ethods; not in your set method. Something like this:
private var _myProperty:MyType;
private var _myPropertyChanged:Boolean = false
[Bindable('myPropertyChanged')]
public function get myProperty():MyType
{
return _myProperty;
}
public function set myProperty(value:MyType):void
{
_myProperty = value;
_myPropertyChanged = true;
invalidateProperties();
invalidateDisplayList();
invalidateSize()
invalidateSkinState(); // spark comps only
dispatchEvent(new Event('myPropertyChanged'));
}
The invalidate methods will force the component lifecycle method to rerun during the next render event and you can use code like this in the relevant method:
if(_myPropertyChanged == true){
_myPropertyChanged = false;
// do other processing
}
You can use get and set accessor methods. More details is here.
In you case it is something like:
private var _myProperty:MyType;
public function set myProperty(value:MyType):void
{
_myProperty = value;
// he best way is to place myMethod body here
myMethod(_myProperty);
}
[Bindable]
public function get myProperty():MyType
{
return _myProperty;
}
This is how I would do it. Create a setter function that calls the method you propose:
var _mystatus:Number = 0;
function set mystatus(val:Number):void
{
_mystatus = val;
alertfunction();
}
function get mystatus():Number
{
return _mystatus;
}

AS3 reflection. How to find out if a method is overridden?

Is it possible to use AS3 reflection to find out if a method was overridden?
I need a method like:
protected function isOverriden(methodName:string) : bool
{
//magic here!
//...
return awesomeLocalVariable;
}
So, I pass in the method name as a string and the isOverridden method yields true only if and only if the object has a method of that name and it is overridden from its original implementation.
Any idea on how to code the magic there?
Thanks.
Edit: As requested, the context of the problem:
I'm building a framework for creating AS3 games. I want to provide "components" for my game objects, each component provides functionality to the game object it is applied. Components are based on events (onClick, onUpdate, onShapeCollision, etc) I need this code in the Component class, so I can register only the events that the actual Component-derived class implements (overrides).
Example component:
public class CTrace extends ScriptComponent
{
public override function onClick(event:MouseEvent = null):void
{
trace(Owner.Id);
}
}
The framework should register the onClick method as the event handler for the MouseEvent.CLICK event because it overrides the default implementation.
Why do I need the default implementation? Because I want the classes to override the supported methods so there will be a compile time error if the user tries to use an unsupported event.
Does that makes sense?
Here is a try. The function is static and it may be used to check any class or object regardless of the class in which it is implemented. If you give it the type, it will use it, if you give it an instance, it will get the type by itself. The inner logic is just to check the given type description for the function we are looking for, if such exists and is declared by the class, it will check if the method also exists in the parent. And if both exists, enjoy, it means it is overridden.
/**
* Returns true only if the method name given is declared by
* the source class, and any parent class.
*/
static public function isOverridden(source:*, methodName:String):Boolean {
var parentTypeName:String = getQualifiedSuperclassName(source);
if (parentTypeName == null) {
return false;
}//if
var typeName:String = getQualifiedClassName(source);
var typeDesc:XML = describeType(getDefinitionByName(typeName));
var methodList:XMLList = typeDesc.factory.method.(#name == methodName);
if (methodList.length() > 0) {
//Method exists
var methodData:XML = methodList[0];
if (methodData.#declaredBy == typeName) {
//Method is declared in self
var parentTypeDesc:XML = describeType(getDefinitionByName(parentTypeName));
var parentMethodList:XMLList = parentTypeDesc.factory.method.(#name == methodName);
return parentMethodList.length() > 0;
}//if
}//if
return false;
}//isOverridden
And just in case it is needed, the imports required for it to work:
import flash.utils.describeType;
import flash.utils.getDefinitionByName;
import flash.utils.getQualifiedClassName;
import flash.utils.getQualifiedSuperclassName;
And to use it:
trace(isOverridden(ChildrenClass, "overriddenMethod")); //true
trace(isOverridden(ChildrenClass, "onlyChildMethod")); //false
trace(isOverridden(ChildrenClass, "onlyParentMethod")); //false
If you are asking inside the same object you can
overriden = (this[stringNameOfMethod] instanceOf Function && super[stringNameOfMethod] instanceOf Function);
If not, try using describeType. Check if there's a method with the name and check the "declaredBy" attribute. Voila!
http://livedocs.adobe.com/flash/9.0/ActionScriptLangRefV3/flash/utils/package.html#describeType()
Sorry, I'm in the middle of a migration from CS3 to Flash builder so for now I cannot ensure my ideas work correctly. But I will be back.

AS3 driving me nuts

Ok here is what I am currently trying to do. I have a class called vdata.as which takes 2 paramaters both are strings sent from the main stage. Parameter one is the location for an XML file that I need to open and read. The second parameter is the name of the video I am currently looking for.
Now I can get the data from the XML file and display it with out any issue if its called from my class but when I try to access any of it from the stage I get undefined.
import flash.net.*;
import flash.display.*;
import flash.events.*;
public class videoData
{
private var mName:String;
private var mLink:String;
private var mCategory:String;
public static var elementArray:Array;
// Constructor
public function videoData(xmlPath:String,xmlVidSrc:String,pMC:MovieClip)
{
pXmlPath = xmlPath;
pXmlVidSrc = xmlVidSrc;
xmlloader = new URLLoader();
elementArray = new Array();
}
public function getXML()
{
XMLData();
}
private function XMLData()
{
xmlloader.load(new URLRequest(pXmlPath));
xmlloader.addEventListener(Event.COMPLETE,parseXMLData);
}
private function parseXMLData():void
{
var x:XML = new XML(xmlloader.data);
Init(x);
}
private function Init(m:XML):*
{
var i:Number;
for(i=0; i<m.videos.videoname.length(); i++)
{
if(m.videos.videoname[i].#name == pXmlVidSrc)
{
videoData.elementArray.push(m.videos.videoname[i].#name);
videoData.elementArray.push(m.videos.videoname[i].#category);
videoData.elementArray.push(m.videos.videoname[i].link.#url);
}
}
}
}
When I call it from the main stage the code is as follows.
var xData:videoData = new videoData(xmlPath,vidSrc,this);
xData.getXML();
then when I try to access any elements of videoData.elementArray they come up undefined...
Im just smacking my head on my desk trying to figure this out any help would be great.
Why is elementArray a static var, you only need to make it public to use it outside the function.
I'm quite confusing but you may want to try a debugging tool like "De MonsterDebugger", I would start by tracing xmlloader.data in the parseXMLData function.
"addEventListener" doesn't "fire"...the event does. You'll need to add a boolean to state for the stage that elementArray has been populated and set that after the init function.
Is elementArray something that needs to be true across all instances of videoData? If not, it shouldn't be static. You can use MovieClip(this.root).xData to access that instance of the video class from one of your other classes.
If the event has completed and the array is still empty - then it wasn't populated by your parser. You can also do checks to see if the elementArray.length > 0.
EDIT in response to comment:
as a public member or preferably a read-only property make a boolean variable:
var parseComplete:Boolean;
Set it to false in your constructor.
Then, after your call to "Init" in your Event.COMPLETE callback set:
parseComplete=true;
Then make sure parseComplete == true before you ever access elementArray. If you're waiting for the parser to complete you might want to set a timeout or some sort of try/catch mechanism just in case there are any unforeseen errors that would cause some sort of:
while( !xData.parseComplete ) { }
To loop indefinitely. It all depends on the usage. Personally I'd probably add a callback from the event listener to the stage to trigger whatever is supposed to happen next.