I'm trying to build a Flex application using the Robotlegs framework and don't know how to deal with the creation of mediators (-> onRegister being called) only after the creationComplete event of the view component.
My application loads several XML files at startup, models are created from the files and then dispatch events with the data.
Problem: When "loading" embedded/local files at startup, the model events are dispatched way before the mediators listeneres are added, although they're mapped before triggering the initial data load in the main context.
Is anybody using robotlegs with flex and has foud a "cleaner" way around this, than manually dispatching an event in the mediators onRegister? As doing so the "automatic" mediation would not really be automatic anymore ...
Edit:
Minimal example code:
Context:
override public function startup( ):void{
mediatorMap.mapView( EditorMenu, EditorMenuMediator );
commandMap.mapEvent( ContextEvent.STARTUP, LoadConfigurationCommand );
dispatchEvent( new ContextEvent( ContextEvent.STARTUP ) );
}
LoadConfigurationCommand:
[Inject] public var configurationService:IXMLLoader;
override public function execute():void{
configurationService.loadXML();
}
ConfigurationService:
public function loadXML(){
trace( "xml loaded" );
dispatch( new XMLLoadedEvent( XMLLoadedEvent.CONFIGURATION_LOADED, result ) );
}
EditorMenuMediator:
override public function onRegister( ):void{
trace( "menu onregister" );
addContextListener( XMLLoadedEvent.CONFIGURATION_LOADED, handleXmlLoaded, XMLLoadedEvent);
}
The trace "menu onregister" is happening way before the trace "xml loaded", so the mediator's not listening when the XmlLoadedEvent is dispatched.
I approach this with the StateMachine and grab control of the order of operations. Another approach here would be to add the listener, but also inject the model and check it for data in onRegister. Either approach should put you in front of the race conditions.
I'm not expert in RobotLegs (I'm actually a Parsley addict), however, if you're using a Mediator pattern, that means that your mediator has direct access to the view itself. How about you create an interface class for all your views that has an init public function which your mediator could call after the onRegister.
Maybe Robotlegs has another way of doing it with metadata or events or something, but this is still valid.
Related
I am looking for a way by which I can capture all events (specifically keyboard/mouse events) in a JavaFX application. Is there anything similar to AWTListener in JavaFX? In Java, by creating a AWTListener and adding it to default toolkit I can capture all the events.
Adding some background
I am involved with Marathon, a test automation tool for Java/Swing applications, and currently in the process of evaluating how complicated it is to add JavaFX support. By design, I do not have access to the JavaFX application itself. For Java/Swing we use javaagent to load Marathon hook into the AUT and use AWTListener to listen for all events. I suspect that something similar should be possible for JavaFX.
Not sure how exactly your goal can be achieved, but I would start by taking a look at the EventDispatcher of a Scene.
final EventDispatcher eventDispatcher = scene.getEventDispatcher();
scene.setEventDispatcher( new EventDispatcher()
{
#Override
public Event dispatchEvent( final Event event, final EventDispatchChain tail )
{
System.out.println( "Dispatching event [" + event + "]." );
return eventDispatcher.dispatchEvent( event, tail );
}
} );
Context
I have a custom Event Entity which has several child Entities: Problem and Maintenance (and few others but those two should be enough to describe the problem) entity classes inherit from Event entity class.
The addAction(), seeAction() and modifyAction() of ProblemController and MaintenanceController are (obviously) very similar but with some differences.
I want to have a button to display the see view of an Event, no matter if it is a Problem or a Maintenance. Same for modify.
For the add action it is a bit different: the user has to say (by clicking on child-specific button) what kind of child he want to add.
How I handle this so far
In my seeAction() and modifyAction(), I just forward the "call" depending on the type of the child:
public function seeAction(Event $event)
{
if($event instanceof \Acme\EventBundle\Entity\Problem){
return $this->forward('AcmeEventBundle:Problem:see', array('event_id' => $event->getId()));
}
elseif($event instanceof \Acme\EventBundle\Entity\Maintenance){
return $this->forward('AcmeEventBundle:Maintenance:see', array('maintenance_id' => $event->getId()));
}
}
I have no Event::addAction() but I have a Event::addCommon() which gathers the common parts of the addAction of Problem and Maintenance. Then I call this Event::addCommon() with Controller inheritance.
class ProblemController extends EventController
{
public function addAction(MeasurementSite $measurementSite)
{
$problem = new Problem();
$problem->setMeasurementSite($measurementSite);
$form = $this->createForm(new ProblemType($measurementSite), $problem);
$response = parent::addCommon($problem, $form);
return $response;
}
Problem
All this looks pretty ugly to me. If I want to share common things between Problem::seeAction() and Maintenance::seeAction(), I will have to call an Event function, but Event already forwarded something!! Information jumps from Parent to Child and vice versa...
I would like to know what is the proper way to manage this problem?
I looked a bit at setting Controller as a service, using PHP Traits, Routing inheritance but I couldn't extract anything clear and clean from this research...
I can see how you might end up chasing your tail on this sort of problem.
Instead of multiple controllers, consider have one EventController for all the routes along with individual ProblemHelper and MaintainenceHelper objects. The helper objects would have your add/see/modify methods and could extend a CommonHelper class.
Your controller would check the entity type, instantiate the helper and pass control over to it.
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.
I am trying to dispatch a custom event from one flex module to another.
The code which dispatch the event is as below
Application.application.Destination.child.dispatchEvent(
new AlgoEvent(AlgoEvent.GETFROMPARENT_LOCAL_EVENT));
here AlgoEvent is a custom event
on the other side the module which catches and handles the event has this code:
public function sendParametersToChild(e:AlgoEvent):void
{
//some codes
}
but when the statement Application.application.Destination.child.dispatchEvent(new AlgoEvent(AlgoEvent.GETFROMPARENT_LOCAL_EVENT)); is executed the debugger give the following run time exception:
TypeError: Error #1034: Type Coercion failed: cannot convert resources.events::AlgoEvent#4182239 to resources.events.AlgoEvent.
at flash.events::EventDispatcher/dispatchEventFunction()
at flash.events::EventDispatcher/dispatchEvent()
at mx.core::UIComponent/dispatchEvent()[C:\autobuild\3.2.0\frameworks\projects\framework\src\mx\core\UIComponent.as:9298]
at components::Destination/sendTimeToChild()[E:\FlexProjects\MyApp\src\components\Destination.mxml:99]
at components::Destination/updateParameters()[E:\FlexProjects\MyApp\src\components\Destination.mxml:206]
at components::Destination/__CreateBasketButton_click()[E:\FlexProjects\MyApp\src\components\Destination.mxml:558]
I am not able to identify what is going wrong here.
Please help to solve this problem
This is my Event class
public class AlgoEvent extends Event
{
public static const GETFROMPARENT_LOCAL_EVENT:String = "getfromparent_local";
private var eventType:String;
public function AlgoEvent(eventType:String, bubbles:Boolean=false, cancelable:Boolean=false)
{
super(eventType,bubbles,cancelable);
this.eventType=eventType;
}
}
While debugging am getting error in this funcion of UIComponent class
override public function dispatchEvent(event:Event):Boolean
{
if (dispatchEventHook != null)
dispatchEventHook(event, this);
return super.dispatchEvent(event);
}
Excaxtly this line gives the error: dispatchEventHook(event, this);
Import the AlgoEvent class in the main application and create a reference to it.
import resources.events.AlgoEvent;
private var dummyEvent: AlgoEvent;
Some explanations for this could be found here: Module domains
If your custom event doesn't carry any special event properties you could workaround your problem by using the standard Event class.
dispatchEvent(new Event(AlgoEvent.GETFROMPARENT_LOCAL_EVENT));
I had the same problem when dispatching, solved overriding two functions:
override public function clone():Event
{
return new AlgoEvent(type, bubbles, cancelable);
}
override public function toString():String
{
return formatToString("AlgoEvent","type"","bubbles","cancelable","eventPhase");
}
hope it helps out :)
Mr. splash suggested a solution which worked fro me:
Try to make the Custum Event (Algo Event in my case) class known to the main application.
I.e import it in the main application and create a variable of it..
And it works for a main reason>>when we try to communicate betwwen the modules using event dispatching what happens is :the modules are loaded at the run time but the classes like event classes are linked to the modules at the run time..
But the Event class is compiled before the modules are loaded..
application defines a Custum Event Class at compile time, and the module defines its own Custum Event Class when it is published. Then when the application is run, the Custum Event Class dispatched in the application doesn't match the one in the module
swf.
For the problem which is causing this error one can check the link:
http://www.kirupa.com/forum/showthread.php?t=320390
and also
http://www.jeffdepascale.com/index.php/flash/custom-events-in-loaded-swf-files/
Mate framework takes care of all this.
It gives you a global event bus, for all modules in your app.
http://mate.asfusion.com/
Try to override the clone() method in your customized Event,AlgoEvent.
Add the following code to your AlgoEvent.as class and try:
override public function clone():Event{
return new AlgoEvent(eventType,bubbles,cancelable);
}
HTH.
Your custom Event class should look like this:
public class AlgoEvent extends Event
{
public static const GETFROMPARENT_LOCAL_EVENT:String = "getfromparent_local";
public function AlgoEvent(type:String, bubbles:Boolean=false, cancelable:Boolean=false)
{
super(type, bubbles, cancelable);
};
override public function clone():AlgoEvent
{
return new AlgoEvent(type, bubbles, cancelable);
};
};
You should use the Event's inherited type property instead of creating a new one.
Also, the UIComponent has it's own dispatchEvent method, so you don't have to create your own - only if it works differently to the inherited one.
Regards,
Rob
Okay, it must be said that what you're doing, from an architectural standpoint, is wrong. Calling Application.application is bad for so many reason, especially if you're then starting to go down the display tree. The second any of the children changes, your build is now broke, and you won't know that until runtime because it's a module.
What you need is an application framework. A way to increase complexity without decreasing maintainability. There are many out there, but my personal favorite is Parsley. I've used it on many very large projects with much success. The problem you're trying to solve right now, dispatching one event where the other module listens for it, is extremely trivial (can be done in about 3 lines of code).
I recommend you look it over as well as my presentation on an introduction to parsley.
The invalidate/commitProperties model used by mxml components is very useful, in my experience, and I'd like to be able to make use of it in domain model objects in my actionscript applications. How can I go about adding lifecycle events like that to my objects? Is there a global object lifecycle manager?
As noted by Robert Bak, you're essentially on your own to implement such a mechanism for non-UI components.
I've found this a very useful technique to use on model classes, since it can dramatically reduce the "thrashing" of bound-property updates when your model classes are not simple data transfer objects - i.e. they have any kind of multi-property logic encapsulated within them.
Since my use-case is for model objects, I didn't need all the methods of IInvalidating.
Here's my particular implementation as a starting point for your own efforts. Note that this comes from a "base model class" we use called RAFModel and that this is for the Flex 4 SDK.
// INVALIDATION AND COMMITPROPERTIES PATTERN
private var invalidatePropertiesFlag:Boolean;
public function invalidateProperties():void
{
if (!invalidatePropertiesFlag)
{
invalidatePropertiesFlag = true;
invalidateModelObject(this);
}
}
protected function commitProperties():void
{
// override this
}
// -- INVALIDATION SUPPORT
public static var invalidObjects:Dictionary = new Dictionary(true);
public static var validatePending:Boolean = false;
public static function invalidateModelObject(obj:RAFModel):void
{
invalidObjects[obj] = true;
if (!validatePending)
{
validatePending = true;
FlexGlobals.topLevelApplication.callLater(validateObjects);
}
}
protected static function validateObjects():void
{
var invalidQueue:Dictionary = invalidObjects;
// start a fresh tracker for further invalidations
// that are a side effect of this pass
invalidObjects = new Dictionary(true);
// ready to receive another call
validatePending = false;
for (var o:* in invalidQueue)
{
var rm:RAFModel = o as RAFModel;
if (rm)
{
// clear the flag first, in case we're reentrant
// on any given instance
rm.invalidatePropertiesFlag = false;
rm.commitProperties();
}
}
}
Invalidation and commitProperties isn't linked to MXML (you can use it with as components) but it is linked to the flex managed visual component lifecycle (as they are the only ones which need to be synchronized with the flash frame by frame rendering). So unless you're talking about visual components it will not work out of the box.
But if you're looking to implement the same mechanism for your non-visual classes, you should probably start by implementing IInvalidating (docs) and creating a mechanism that calls the validateNow() function when the validation needs to be done.
The Flex Component LifeCycle is designed to handle a User Interface Component's creation, destruction, and changes during the time in between. I, personally, do not find the approach appropriate for non-User Interface components.
You could, if you wanted, extend UIComponent in your domain model objects and then add that domain model as a child to a container. it would then go through the Flex Component LifeCycle validation phases (commitProperties, updateDisplayList, and measure).
But, I would not recommend that approach.