Is there any way to add a valueCommit lifecycle to non-mxml components in Actionscript? - apache-flex

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.

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.

Is interception worth the overhead it creates?

I'm in the middle of a significant effort to introduce NHibernate into our code base. I figured I would have to use some kind of a DI container, so I can inject dependencies into the entities I load from the database. I chose Unity as that container.
I'm considering using Unity's interception mechanism to add a transaction aspect to my code, so I can do e.g. the following:
class SomeService
{
[Transaction]
public void DoSomething(CustomerId id)
{
Customer c = CustomerRepository.LoadCustomer(id);
c.DoSomething();
}
}
and the [Transaction] handler will take care of creating a session and a transaction, committing the transaction (or rolling back on exception), etc.
I'm concerned that using this kind of interception will bind me to using Unity pretty much everywhere in the code. If I introduce aspects in this manner, then I must never, ever call new SomeService(), or I will get a service that doesn't have transactions. While this is acceptable in production code, it seems too much overhead in tests. For example, I would have to convert this:
void TestMethod()
{
MockDependency dependency = new MockDependency();
dependency.SetupForTest();
var service = SomeService(dependency);
service.DoSomething();
}
into this:
void TestMethod()
{
unityContainer.RegisterType<MockDependency>();
unityContainer.RegisterType<IDependency, MockDependency>();
MockDependency dependency = unityContainer.Resolve<MockDependency>();
dependency.SetupForTest();
var service = unityContainer.Resolve<SomeService>();
service.DoSomething();
}
This adds 2 lines for each mock object that I'm using, which leads to quite a bit of code (our tests use a lot of stateful mocks, so it is not uncommon for a test class to have 5-8 mock objects, and sometimes more.)
I don't think standalone injection would help here: I have to set up injection for every class that I use in the tests, because it's possible for aspects to be added to a class after the test is written.
Now, if I drop the use of interception I'll end up with:
class SomeService
{
public void DoSomething(CustomerId id)
{
Transaction.Run(
() => {
Customer c = CustomerRepository.LoadCustomer(id);
c.DoSomething();
});
}
}
which is admittedly not as nice, but doesn't seem that bad either.
I can even set up my own poor man's interception:
class SomeService
{
[Transaction]
public void DoSomething(CustomerId id)
{
Interceptor.Intercept(
MethodInfo.GetCurrentMethod(),
() => {
Customer c = CustomerRepository.LoadCustomer(id);
c.DoSomething();
});
}
}
and then my interceptor can process the attributes for the class, but I can still instantiate the class using new and not worry about losing functionality.
Is there a better way of using Unity interception, that doesn't force me to always use it for instantiating my objects?
If you want to use AOP but are concerned abut Unity then I would recommend you check out PostSharp. That implements AOP as a post-compile check but has no changes on how you use the code at runtime.
http://www.sharpcrafters.com/
They have a free community edition that has a good feature set, as well as professional and enterprise versions that have significantly enhanced feature sets.

Flex: Which way should I add this event handler?

I use a unit of work pattern a lot in my flex projects. I'll have a class that might call a web service, put the data in a sqlite db, refresh a model with the data then raise an event.
I usually call these inline and pass in some singleton classes:
protected function CareerSynced():void
{
var process:ProcessWorkouts = new ProcessWorkouts(_dataModel, _trainerModel, _databaseCache, _database.Conn);
process.addEventListener("AllWorkoutsProcessed", AllWorkoutsProcessed);
process.UpdateAllUnprocessed();
}
I'll then get the response like this:
private function AllWorkoutsProcessed(event:DataReceivedEvent):void
{
//do something here
}
My question is, am I adding that event listener correctly? I think I might be causing a memory leak, but I'm not sure. I've also thought about using a weak reference. I'm confused about when to use them. Would this be one of those cases?
Should it be like this?
process.addEventListener("AllWorkoutsProcessed", AllWorkoutsProcessed,false, 0, true);
I would either go with the weak reference or just remove the listener:
private function AllWorkoutsProcessed(event:DataReceivedEvent):void
{
event.target.removeEventListener("AllWorksoutsProcessed",AllWorkoutsProcessed);
}
I could list out my reasons but I'll just point you to this.

Using asMock, how can I satisfy a concrete and interface requirement in SetupResult.forCall

The ValidationManager has a public Dictionary for storing UI components that implement the IValidatable interface.
I am testing a command class that needs an instance of ValidationManager and I want it to fail the validations. So I override the ValidationManager's "validateItem()" method like so:
var validationManagerRepos:ValidationManager = ValidationManager(mockRepository.createStub(ValidationManager));
var validationItem:IValidatable = IValidatable(mockRepository.createStub(IValidatable));
var validatableItems:Dictionary = new Dictionary();
validatableItems[validationItem] = false;
SetupResult.forCall(validationManagerRepos.validateItem(validationItem)).returnValue(false);
My problem is in the execute method of the command. It checks to see if the validationItem is both a DisplayObject (isVisble) and IValidatable. Any slick way to stub a typed object AND an interface? Or do I just need to create an instance of some existing object that already satisfies both?
for (var iVal:Object in validationManager.validatableItems)
{
if (isVisible(DisplayObject(iVal)))
{
passed = validationManager.validateItem(IValidatable(iVal));
eventDispatcher.dispatchEvent(new ValidationEvent(ValidationEvent.VALIDATE_COMPLETED, IValidatable(iVal), passed));
if (!passed)
{
allPassed = false;
}
}
}
I'm fairly sure you can't do both within asMock. It's a limitation of the Flash Player because of lack of polymorphism.
I believe what you'll have to do is create a testing object that does both (extend DisplayObject and implement IValidatable) and create a mock object of that.
The concept of a "multimock" is certainly possible, but floxy (the framework that asmock uses to generate dynamic proxies) doesn't support it. I previously considered adding support for it, but it would be difficult to expose via the various Mock metadata and there's be other issues to worry about (like method name clashes).
I agree with J_A_X's recommendation of creating a custom class and then mocking that.

Can I control multiple instances of movieclips in a loaded swf at once?

I am loading an swf created in flash professional cs5 via the loader class into a flex 4.1 application. The flash file contains multiple movieclips that are exported for actionscript and those movieclips exist in many instances throughout the movie.
Iterating through everything, comparing class types seems to be the most easy but also the most redundant way to solve this. Is there any way of using the class name as a kind of global selector to access the clips?
I could also make the sub-clips in the flash listen for an event on which they perform an action, but I am not really sure what might be best.
In cases like these, I find that a good way to solve the problem is to create a statically accessable class that manages instances of other classes that are registered with it on instantiation. As an example...
public class GlobalStopper{
private static var clips:Array = [];
public static function add(mc:MovieClip):void{
clips.push(mc);
}
public static function stop():void{
var mc:MovieClip;
for(var i:int = 0, ilen:int = clips.length ; i < ilen ; i++){
mc = clips[i] as MovieClip;
if (mc) mc.stop();
}
}
}
and...
public class GloballyStoppableMovieClip extends MovieClip{
public function GloballyStoppableMovieClip(){
GlobalStopper.add(this);
}
}
Any and all instances of GloballyStoppableMovieClip are instantly registered with the GlobalStopper, so calling
GlobalStopper.stop();
...will stop all registered movieclips.
You can add in any other functions you want. Furthermore, instead of having add accept MovieClip instances, you could have it accept IStoppable or IPlayable objects that implement public functions stop() and play() that your movieclip subclass (or non-movieclip object that also might need to stop and play!) then implements.
But as for jQuery-like selectors? Not really the way I'd handle this particular issue.
i guess typing it out did the trick. i used the event solution:
in the root timeline i placed a function like this:
function cause():void {
dispatchEvent(new Event("do stuff",true));
}
and in the library clip's main timeline goes:
DisplayObject(root).addEventListener("do stuff", function (e:Event=null) {
... whatever ...
});
this is dirty but you get the idea.

Resources