I'm excited about the new module implementation, but I have some questions about the way it's getting implemented in Cairngorm 3 so far. It would seem that the way InsyncContext is architected, the modules are hard coded in the app. The way I've implemented modules in the past was to make them completely data-driven, so I can make the available modules user profile-driven.
I'm interested in anyone's thoughts on how this could be done with Cairngorm 3. Maybe some sort of Factory class that provides a new ParsleyModuleDescriptor with passed parameters for the module URL, etc?
Jeff
Hi Jeff,
That's a good question and I think modules are a good candidate for runtime configuration. We've also seen the requirement you mention, where different users are entitled to access different modules. Also, it can be desirable to release new modules into production without needing to redeploy the shell application that loads them.
So how could this be implemented? Well both Spring ActionScript and Parsley provide a means of configuring a context at runtime from an XML file. With both frameworks this system is commonly used for configuring the Flex logging framework, but the same mechanism could be used for configuring module descriptors. What is needed is a class for describing the module and a component for loading and rendering it.
Re. the Cairngorm module library, it's early days. This is one of the more experimental parts of Cairngorm and its design is very much up for debate. The intention is to provide some features not included in the Flex SDK, such as:
At the same time, features of the standard Flex ModuleLoader shouldn't be lost, such as the ability to declare multiple instances of the same module at different parts of the UI.
Do these requirements resonate with you? Do you have thoughts on how you'd like to see them solved? Have you solved them before? It would be nice to stay as close to convention as possible.
Best,
Tom
Thanks Tom,
The way I solved this previously was via a LoadModule event, which was
derived from the profile downloaded for the logged in user. This was based
on a simple "there can be only one" module of a given type. The module was
loaded into a ViewStack and as part of its instantiation, loaded necessary
data assets. The issue I ran into as the unloading of the module. Some
modules would load/unload with no memory leaks a some wouldn't. I spent
quite a bit of time with the profiler trying to figure out why. Binding
would seem to be a possible liability as when using quick and dirty MXML
binding, the references were hard.
I was very excited to read the Parsley docs about modules and contexts. If
the context that the module is relying on is destroyed on unload, that would
seem to address the issues with the module not unloading cleanly, so long as
the view is bound to model objects with weak references.
In the case of Cairngorm and Parsley, injection should be used instead, but
the question becomes how to perform that injection in ActionScript vs. MXML.
That's what leads me toward a global Module factory class that would deliver
a fully configured module based upon a descriptor and add that to the
appropriate landmark in the view hierarchy.
Jeff
I decided to throw caution to the winds and attempt to port an existing application over to CG3 with modules - but with the modules hard-coded like they are in the sample CG3 app. I'm treating this as sort of a tutorial to get up to speed with CG3 and Parsley. I've been able to get the modules to load correctly but run aground on commands. Events fired in the module's context don't seem to want to communicate to Comands declared in the same context. Basically the messaging 'sub-system' isn't working and I haven't been able to figure out what I'm doing wrong.
Any advice appreciated.
Jeff
Thanks Alex,
I believe that I've got all the dependencies downloaded from the trunk and in the build path:
Module
Navigation
Integration
IntegrationRPC
Task
parsley-complete-flex3-2.1.0.swc
spicelib-complete-flex-2.1.0.swc
Here's the PM that is dispatching the event:
ilc.modules.timeline.presentation {
import flash.events.EventDispatcher;
import ilc.modules.timeline.GetDecadesEvent; [
Event(name="getDecades", type="ilc.modules.timeline.GetDecadesEvent")] [ManagedEvents(
"getDecades")]
public class TimelinePM extends EventDispatcher {
public function TimelinePM() { getDecades(); }
public function getDecades():void
{ dispatchEvent(
new GetDecadesEvent()); } } } Here's the Command class: package { import ilc.modules.timeline.GetDecadesEvent; import ilc.modules.timeline.domain.Decades; import mx.collections.ArrayCollection; import mx.rpc.AsyncToken; import mx.rpc.events.FaultEvent; import mx.rpc.events.ResultEvent; import mx.rpc.remoting.mxml.RemoteObject; public class GetDecadesCommand { public function GetDecadesCommand() { } [MessageDispatcher] public var dispatcher:Function; [Inject] public var decades:Decades; [Inject(id= 'remoteService')] public var service:RemoteObject; [Command] public function execute(event:GetDecadesEvent):AsyncToken { trace('getting decades'); return service.getDecades() as AsyncToken; } [CommandResult(selector= "getDecades")] public function serviceResult(event:ResultEvent):void { decades.decades = new ArrayCollection(event.result as Array); } [CommandFault(selector= "getDecades")] public function showError(fault:FaultEvent):void { trace(fault.toString()); } } } Do you see anything obvious? Jeff
I'm beginning to think there's something up with the way I'm creating the child contexts - as my PM that should have been injected into one of the views in the heirarchy, but it's null. It should be as simple as creating a new config,mxml in the module itself - should it not?
Jeff
I think that messaging is normally scoped to a context hierarchy, so in order for a message sent in the context of a module to be received by handlers in another context, those contexts must be within a hierarchy. In other words, when you build the context for your module, Parsley needs to be able to find the parent context too. It tries to do this automatically with Parsley 2.1, but in some cases it cannot succeed, so you need to specify the parent context manually via the FlexContextBuilder.build() method.
Thanks Tom (and Alex),
I'll check the Parsley docs on how to refer to the parent context. In the case of modules it would seem to me that the model objects would want to live exclusively in the context of the module - am I right?
What I am noticing in the case of my module is that the context is available at the module view heirarchy level but not available in any child views, even if I have included them in the context definition and have fired off the 'configureIOC' event from within the view. In the app I am converting, the module contains another view which I am injecting my PM into, and the PM keeps coming back null.
Even still, though, when I am firing my event which should result in command class getting executed in my module, the messaging does not appear to be working.
Jeff
Tom,
A further development: if I use the MessageHandler metatag instead of Command, it works (at least in the immediate context of the module). Am I correct that the Command extensions are in the Integration.swc? I checked out the integration libraries have referenced the Integration.swc from my test project. Is there another dependency that I'm missing?
Jeff
Jeff,
please have a look into the InsyncModularExtended sample applications. They also use Commands, the IntegrationRPC library and show you how to set it up. It works there but I'd be interested if there's anything different in your scenario that would make the integration library fail. Maybe you're missing to initialize the integration RPC library in your entry level MXML. Check out the Insync samples. They have commands in the contacts module.
Alex,
I did not have the integration libs tag in my module - only at the top level of the app That was it!!!!!! - thank you and Tom for hanging in there on this.
However, I'm still not getting my model injected into a view child of my module. I switched to ViewConfigurationEvent.CONFIGURE_VIEW to ensure that there was nothing up with my spelling but no, my child view still isn't getting injected with my model, even though I have the view in my context definition.
Jeff
Tom, Alex,
Although my understanding of C3 is progressing, it is far from what it should be. The most frustrating part of this is getting the data retrieved by a Command back into the model. It appears that the PM model that I might be injecting is not singleton or possibly may exist in more than one scope. I've tried passing a reference to my target model in a Managed event, but so far no dice. Almost makes me wish for a ModelLocator.
Jeff
Hi Jeff,
Have you studied the Parsley developer manual for more overview? Also check if this could be causing different instances of your PM. Enable logging on Parsley to see if any message handler runtime errors have been thrown or use Parsley's MessageError tag.
Alex,
I admit to being completely confused at this point. I fire off a managed
event which invokes a Command, which returns an array, everything seems to
be fine, but the reference if the model object that's being updated is not
the one I referred to. It's almost as if the context and my normal Flex
view aren't talking to each other. It's like the context and view are in
parallel universes. And I still have the issue where subviews aren't
getting processed by the context upon instantiation.
I'm quite sure I am **** something wrong - I'm comparing my source code
against the sample insync extended app and trying to identify where I've
gone astray...
Jeff
Alex, Tom,
I'm making some incremental headway but it is slow going as I try to figure out whether the behavior I'm seeing is the result of my inexperience with Parsley or possible bugs in the framework itself. Here's what I've been able to accomplish so far:
I've got my shell app working with modules being loaded on demand - not dynamic modules mind you - but hard coded at this point. Works fine.
I've also been able to get my Commands to send results back to the view - this works but things go awry when I have a subview to the module. I can inject the child view, but the child view keeps getting added/removed/added into the context and my RPC calls are duplicated. In the end, the bindings from my model to the child view's components don't get executed.
Jeff
Hi Jeff,
sorry but I have to repeat myself. Have you read the Parsley forum post between Xavi and Jens I pointed to above? There are also more threads on that topic in the Parsley forum if you search for it. Your problem sounds exactly like that. It's caused by multiple addedToStage events. There are workarounds.
UPDATED: Ok, doesn't mind. I just understand that you were simply meaning to put the tag <cairngorm:CairngormIntegrationLib /> inside the module.
Thanks anyway!! ![]()
Hi Jeff, I'm having the exact same problem in a project; if I change [Command] by [MessageHandler] it works correctly.
The problem is that I have the integrationlib.swc added to the module. What do you mean with "I did not have the integration libs tag in my module"?
Thanks!
Hi Jeff,
I haven't had time to work on this library myself but have a few opinions about how it should change. It's being used on a few other Adobe projects, so I expect some improvements based on their experiences.
I'd like the design to change in this way:
1. Module loading abstracted by an IContent (or something) interface instead of overloading IModuleInfo. Then different implementations can be used for modules, modules with compiled CSS, or other configurations. Some projects need to use special authentication services before loading each module, so that kind of customization could be hidden behind the interface. Instances of these classes could be declared in MXML or XML config files, with the latter being suitable for dynamic modules.
2. Change the ViewLoader component so it works for multiple instances of the same module, and so the behavior is more similar to ModuleLoader in Flex.
3. Write a new stack container for modules, so that only a single loading-overlay and error-state child is present. If you place 10 ViewLoaders in a view stack, you end up with a loading overlay child for each module.
Best,
Tom
Hi Jeff,
Did you notice that commands have been incorporated into the Parsley 2.2 release candidate 2:
http://www.spicefactory.org/parsley/download.php
Jens Halm made some definite improvements over the implementation in Cairngorm 3, so I'd recommend having a look at those. The short-lived command objects are particularly nice. It looks like the Parsley 2.2 developer manual is still in progress, but a description of the new command features can be found here:
http://www.spicefactory.org/parsley/docs/2.1/manual/roadmap.php#comman ds
Best,
Tom
Thanks, Tom.
My particular use case is pretty darned simple - user profile maps to
available modules and navigation reflects this. When navigated to for the
first time, the module is loaded into a view stack. In my app, there's only
area that loads modules. So what we really have is a shell application with
modules loaded on demand.
I guess my question is, would a simpler approach be more appropriate for my
app? Conceivably it could be as simple is sending a message that a certain
module should be loaded (with it's url, etc), and the selectedIndex of the
ViewStack changed accordingly.
Jeff
North America
Europe, Middle East and Africa
Asia Pacific