Archive for March, 2011

Modular flex (4) applications 101

March 27, 2011

How to use modules in the same project:

  1. Define an interface for your module as the following. The whole point of modular app is to program to abstract interfaces so that different parts of your app have less dependency on one another; it also makes it easier to test:package
    {
    import flash.events.IEventDispatcher;
     

    public interface IInnerModuel1 extends IEventDispatcher
    {
    function foo():void
    }
    }

  2. Create a new module by right-click on the project on the Package Explorer> New > MXML Module.  Flex then generates a spark module (s:Module) for you. The module can has its own namespace; if it does, that means the resulting .swf file will be compiled into a subfolder that corresponds to its namespace structure, like  “/debug-bin/com/mh/test/flex/modules/MyInnerModule1.swf” or something.<s:Module xmlns:fx="http://ns.adobe.com/mxml/2009"
    xmlns:s="library://ns.adobe.com/flex/spark"
    xmlns:mx="library://ns.adobe.com/flex/mx"
    implements="IInnerModuel1">
     

    <fx:Script>
    <![CDATA[
    public function foo():void
    {
    myFooLabel.text = "Yo! What's up?";
    }
    ]]>
    </fx:Script>
    ...

    </s:Module>

  3. Add the module to the project by right-click  > Project Properties > Flex Modules > Add and navigate to the .mxml or the .as component.
  4. Load the module for the main app to use. You can do this using the following tag in your main app:
    <s:ModuleLoader id="innerModule1Loader" url="com/mh/test/flex/modules/MyInnerModule1.swf" />
  5. In order to access your module from main WindowedApplication, you can do this:protected function button1_clickHandler(event:MouseEvent):void
    {
     

    if(innerModule1Loader.child == null)
    return;

    var iChild:* = innerModule1Loader.child as IInnerModuel1;
    iChild.foo();

    }

Some links:
How to pass data to modules and access modules from main app and vice versa (Flex 4)

How to use modules from different Flex projects

Step by step of how to use modules in Flex 3

 

Advertisements

Passing on complex object when declaring an ActionScript/MXML component

March 26, 2011

If you want to configure a complex object and pass it on to an ActionScript component when declaring the component, you can use the default property for the latter to pass in the former. Here:

//actionscript
package com.adobe.login.control
{
...
[DefaultProperty("handlers")]
public class SimpleController extends UIComponent
{
[ArrayElementType("com.adobe.login.control.handler.Handler")]
public var handlers : Array;

public function SimpleController()
{
addEventListener( FlexEvent.INITIALIZE, handleInitialize );
}

private function handleInitialize( event : FlexEvent ) : void
{
for( var i : int = 0 ; i < handlers.length ; i++ )
{
var handler : Handler = Handler( handlers[i] );
parent.addEventListener( handler.eventId, handler.execute );
}
}
}
...
}

// mxml
<mx:Application ...
...
<mx:Script>
<![CDATA[
...
[Bindable]
private var pm : MainPM = new MainPM();

]]>
</mx:Script>

<control:SimpleController>
<handler:LoginHandler
client="{ pm }"/>
</control:SimpleController>
...

* vs. Object in ActionScript 3

March 22, 2011

ActionScript 3 doesn’t support Generics like some other languages such as Java. In the case you need to return a Generic type, e.g., your client code is expecting certain type, but the code that provides the service doesn’t know what type it is going to be, you simply use star (*), as opposed to Object:


public function createWindowInstance(type:Class,
allowMultipleInstances:Boolean = false):*
{
...
var windowToOpen:*;
windowToOpen = new ClassFactory(type).newInstance();
spark.components.Window(windowToOpen).open();
return windowToOpen;
}

Passing on objects through constructor

March 20, 2011

I am reading this nice article about architecting your application using Model View Presenter. In the application where you have lots of parts that work together and share some of the plumbings, the way you pass those plumbings around is that you have some centralized objects that are responsible for instantiating both the shared objects and those parts and you pass on the former to the latter  through their constructors:

// A part that asks for the infrastructure such as RPC service, event bus, etc..

public ContactsPresenter(ContactsServiceAsync rpcService,
HandlerManager eventBus, ContactsView<ContactDetails> view,
List<ColumnDefinition<ContactDetails>> columnDefinitions)

...

// The application controller, which constructs most of the parts that do the work.

public AppController(ContactsServiceAsync rpcService,
HandlerManager eventBus) {
this.eventBus = eventBus;
this.rpcService = rpcService;
bind();
}
...

// GWT's bootstrap process calls this "Main" that sets things in motion

public class Contacts implements EntryPoint {
public void onModuleLoad() {
ContactsServiceAsync rpcService = GWT.create(ContactsService.class);
HandlerManager eventBus = new HandlerManager(null);
AppController appViewer = new AppController(rpcService, eventBus);
appViewer.go(RootPanel.get());
}
}

The point is that a working bee (such as the ContactsPresenter) only needs to know the minimal (what he needs to know in order to get his work done). If he needs an rpcService, he does so by simply asking for a rpcService object in their constructors, or maybe have a setter property that can be used by a controller to assign a rpcService to it; but they should not set the rpcService object by themselves, like this:

// an anti-pattern ContactsPresenter object:
this.rpcService = ModelLocator.instance.rpcService;

Think about the scenario when the rpcService object later is to be moved to somewhere else; then we would have to change the code in all the working bees that use this rpcService. If we set the rpcService object through some centralized place (such as in module loader and appController), we would only need to change those few places. Another bad thing about doing so it that then your working bees start to know about too many details (like they know that the rpcService stores in the model locator singleton, which might change later on.) and start to dependent on those assumptions/details.  These couplings make change harder.

A better approach would be to inject those information from outside, such as managed by a few centralized objects, as we did from the beginning.

I think the real world analogy is that a working bee is expected to simply get the work done and not beyond. If he needs something, he would ask his manager, “I need a truck”. It’s the manager’s job to assign him a truck instead of him going out and looking for a truck by himself. (Or maybe not.)

P.S., Later I found out that a more trendy way of saying “to pass on objects” is “to inject the objects”. So the title of this post can then be rephrased as “constructor injection”.

Access main window application (top-level application) in Flex 4/AIR 1.5

March 6, 2011

Sometimes you need to access the top-level/main application (in terms of AIR desktop, it would be the main window, a.k.a the s:WindowedApplication instance that you set as “Default Application” under the default package) directly from your object. The reason for this could be that you have some sort of window manager that you roll out by your own that resides in the “default application” which manages all the windows in your application. You can communicate with the window manager via some event mechanism, or simply directly send a message to it from within your window object, like this:

FlexGlobals.topLevelApplication.windowManager.closeWindow(this);

FlexGlobals and topLevelApplication work on Flex 4, AIR 1.5 and Flash Player 10.