Programacion Modular Flex
Programacion Modular Flex
Creating Modular
Applications
31
You can create modules that you dynamically load in your Flex applications.
Contents
Modular applications overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .1095
Creating modules . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1098
Compiling modules. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1102
Loading and unloading modules . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1110
Using ModuleLoader events . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1119
Passing data . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1128
About modules
Modules are SWF files that can be loaded and unloaded by an application. They cannot be run
independently of an application, but any number of applications can share the modules.
Modules let you split your application into several pieces, or modules. The main application,
or shell, can dynamically load other modules that it requires, when it needs them. It does not
have to load all modules when it starts, nor does it have to load any modules if the user does
not interact with them. When the application no longer needs a module, it can unload the
module to free up memory and resources.
Modular applications have the following benefits:
■ Smaller initial download size of the SWF file.
■ Shorter load time due to smaller SWF file size.
1095
■ Better encapsulation of related aspects of an application. For example, a “reporting”
feature can be separated into a module that you can then work on independently.
Benefits of modules
A module is a special type of dynamically-loadable SWF that contains an IFlexModuleFactory
class factory. This allows an application to load code at run time and create class instances
without requiring that the class implementations be linked into the main application.
Modules are similar to Runtime Shared Libraries (RSLs) in that they separate code from an
application into separately loaded SWF files. Modules are much more flexible than RSLs
because modules can be loaded and unloaded at run time and compiled without the
application.
Two common scenarios in which using modules is beneficial are a large application with
different user paths and a portal application.
An example of the first common scenario is an enormous insurance application that includes
thousands of screens, for life insurance, car insurance, health insurance, dental insurance,
travel insurance, and veterinary pet insurance.
Using a traditional approach to rich Internet application (RIA) design, you might build a
monolithic application with a hierarchical tree of MXML classes. Memory use and start-up
time for the application would be significant, and the SWF file size would grow with each
new set of functionality.
When using this application, however, any user accesses only a subset of the screens. By
refactoring the screens into small groups of modules that are loaded on demand, you can
improve the perceived performance of the main application and reduce the memory use. Also,
when the application is separated into modules, developers’ productivity may increase due to
better encapsulation of design. When rebuilding the application, the developers also have to
recompile only the single module instead of the entire application.
An example of the second common scenario is a system with a main portal application,
written in ActionScript 3, that provides services for numerous portlets. Portlets are configured
based on data that is downloaded on a per-user basis. Using the traditional approach, you
might build an application that compiles in all known portlets. This is inefficient, both for
deployment and development.
By using modules, you can establish an interface that contains portal services, and a generic
portlet interface. You can use XML data to determine which modules to load for a given
session. When the module is loaded, you obtain a handle to a class factory inside the module,
and from that you create an instance of a class that implements the portlet interface. In this
scenario, full recompilation is necessary only if the interfaces change.
Application
ConcreteShell
Manager AbstractClient
Module
AbstractFactory AbstractProduct
ConcreteFactory ConcreteProduct
The ModuleManager manages the set of loaded modules, which are treated as a map of
singletons indexed by the module URL. Loading a module triggers a series of events that let
clients monitor the status of the module. Modules are only ever loaded once, but subsequent
reloads also dispatch events so that client code can be simplified and rely on using the READY
event to know that the module’s class factory is available for use.
The ModuleLoader class is a thin layer on top of the ModuleManager API that is intended to
act similarly to the mx.controls.SWFLoader class for modules that only define a single visual
UIComponent. The ModuleLoader class is the easiest class to use when implementing a
module-based architecture, but the ModuleManager provides greater control over the
modules.
4. In the Application file, use an <mx:ModuleLoader> tag to load each of the modules. You
can also load modules by using methods of the mx.modules.ModuleLoader and
mx.modules.ModuleManager classes.
The following sections describes these steps in detail.
Creating modules
Modules are classes just like application files. You can create them in either ActionScript or by
extending a Flex class by using MXML tags. This section describes how to create modules in
MXML and in ActionScript.
</mx:Application>
5. To convert this new application to a module, replace the <mx:Application> tag with the
<mx:Module> tag so that the resulting code looks like the following:
<?xml version="1.0" encoding="utf-8"?>
<mx:Module xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute">
</mx:Module>
By default, whenever you add an MXML application file to your Flex project, it is runnable
and is added to the list of project application files. All files defined as application files must
reside in your project’s source folder. Because modules must be runnable, they must be in your
project’s source folder.
To determine if a file is runnable, you can examine the file’s icon in the Navigator. A file with
a green arrow is runnable. A file without a green arrow is not runnable.
You can also add a module to your Flex Builder project by selecting File > New > MXML
Component. You then select Module from the Based On drop-down list. While this creates a
new file with a root tag of <mx:Module> rather than <mx:Application>, it does not make the
new file runnable. To make a new MXML component runnable, you must edit the list of
runnable files.
Compiling modules
You compile modules the same way you compile Flex applications. On the command line,
you use the mxmlc command-line compiler. In Flex Builder, you create modules as
applications and compile them by either building the project or running the application.
The result is a SWF file that you load into your application as a module. You cannot run the
module-based SWF file as a stand-alone Flash application or load it into a browser window. It
must be loaded by an application as a module. When you run it in Flex Builder to compile it,
you should close the Player or browser Window and ignore any errors. Modules should not be
requested by the Player or through a browser directly.
For more information on compiling modules on the command line, see “Compiling modules
with the command-line compiler” on page 1103.
For information on compiling modules in Flex Builder, see “Compiling modules with Flex
Builder” on page 1103.
After you compile your module, you try to remove redundancies between the module and the
application that uses it. To do this, you create a link report for the application, and then
externalize any assets in the module that appear in that report. For more information, see
“Reducing module size” on page 1108.
Once you compile a module, you should try to reduce its size by removing dependencies that
overlap with the application that uses the module. For more information, see “Reducing
module size” on page 1108.
Overview
There are two approaches to using modules in Flex Builder projects:
■ Single project — In this approach, you create a single project for your application, and
then add modules to the application list. This approach provides less customization and is
more limiting than creating modules in separate projects. For more information, see
“Using modules in a single project” on page 1103.
■ Multiple projects — In this approach, you create a separate project for each module or a
single project for all the modules. This approach can be appropriate for when you only
have a few modules or need to customize the compilation of your modules. For more
information, see “Using multiple projects” on page 1104.
To add a new module to your project, see “Creating MXML-based modules” on page 1098.
When you run your application, Flex Builder compiles the modules’ SWF files for you. The
SWF files are then loaded into the application at run time. The module SWF files and main
application SWF file are in the same directory.
A related approach is to use a single project for all modules, while keeping the application in
its own separate project. This has some of the drawbacks of using a single project for both the
application and the modules, but has many of the same benefits as using a separate project for
each of the modules.
Using one project for all modules has the following benefits:
■ The module project can be located anywhere in the workspace.
■ You can compile just the modules or just the application, without having to recompile
both at the same time.
Creating projects
When creating a separate project for modules, you change the module project’s output folder
to a directory that is used by the application. You also suppress the generation of wrapper files.
Compiling projects
Compiling multiple projects in Flex Builder is a common operation. First, you choose the
order in which you want the projects to compile and then you compile all projects at the same
time.
If you are using Flex Builder to compile modules, you can use the technique described in this
section if you have a separate project for your modules. In that case, for the main application,
you add the link-report option to the Additional Compiler Arguments field of the Flex
Compiler options. You then use the load-externs option in the same field for each module’s
project to load this report.
The default output location of the link-report option in Flex Builder is the root
directory of the Flex Builder application itself. On Windows, the default directory is
c:\Program Files\Adobe\Flex Builder 2. As a result, if you add the following command to
your compiler options, Flex Builder writes the report.xml file to its root directory:
-link-report=report.xml
If you externalize the module’s dependencies by using the load-externs option, your
module might not be compatible with future versions of Adobe Flex. You might be
required to recompile the module. To ensure that a future Flex application can use a
module, compile that module with all the classes it requires. This also applies to
applications that you load inside other applications.
Debugging modules
To debug the application with modules, you reference the debug SWF file in the source code
during development, and switch to the non-debug SWF file when you are ready to deploy.
For example, when you debug, your calls to the loadModule() method might appear as
follows:
loadModule('ChartModule-debug.swf')
When you have finished debugging and want to deploy the application, you then remove
references to the debug SWF files. Your calls to the loadModule() method might appear as
follows:
loadModule('ChartModule.swf')
The technique described in this section also applies to the SWF file names that you pass to the
ModuleLoader’s url property, or any other way you refer to the URL of the module.
<mx:TabNavigator id="tn"
width="100%"
height="100%"
creationPolicy="auto"
>
<mx:VBox id="vb1" label="Column Chart Module">
<mx:Label id="l1" text="ColumnChartModule.swf"/>
<mx:ModuleLoader url="ColumnChartModule.swf"/>
</mx:VBox>
</mx:TabNavigator>
</mx:Panel>
</mx:Application>
<mx:VBox id="vb1"/>
</mx:Application>
MXML-based modules can load other modules. Those modules can load other modules, and
so on.
The following example shows the cross-domain file that resides on the remote server:
<!-- crossdomain.xml file located at the root of the server -->
<cross-domain-policy>
<allow-access-from domain="loaderservername" to-ports="*"/>
</cross-domain-policy>
For more information about using the cross-domain policy file, see Chapter 4, “Applying Flex
Security,” in Building and Deploying Flex 2 Applications.
<mx:Panel
title="Module Example"
height="90%"
width="90%"
paddingTop="10"
paddingLeft="10"
paddingRight="10"
paddingBottom="10"
>
<mx:TabNavigator id="tn"
width="100%"
height="100%"
creationPolicy="auto"
standin = panel;
removeChild(standin);
}
progress.indeterminate=true;
unload.enabled=false;
reload.enabled=false;
}
if (contains(standin))
removeChild(standin);
}
]]>
</mx:Script>
[Bindable]
public var progBar:String = "";
[Bindable]
public var progMessage:String = "";
<mx:ModuleLoader
id="chartModuleLoader"
url="ColumnChartModule.swf"
progress="progressEventHandler(event)"
/>
</mx:Panel>
</mx:Application>
<mx:ProgressBar
id="progress"
width="100%"
source="{this}"
/>
<mx:HBox width="100%">
<mx:Button
id="load"
label="Load"
click="clickHandler()"
/>
<mx:Button
id="unload"
label="Unload"
click="unloadModule()"
/>
<mx:Button
id="reload"
label="Reload"
click="unloadModule();loadModule();"
/>
</mx:HBox>
</mx:ModuleLoader>
</mx:Application>
This example does not change the ProgressBar’s label property for all events. For example, if
you load and then unload the module, the label property remains at "LOADING 100%".
To adjust the label properly, you must define other event handlers for the ModuleLoader
events, such as unload and error.
Passing data
Communication between modules and the parent application, and among modules, is
possible. You can use the following approaches to facilitate inter-module, application-to-
module, and module-to-application communication:
■ ModuleLoader’s child, ModuleManager’s factory, and Application’s
parentApplication properties — You can use these properties to access modules and
applications. However, by using these properties, you might create a tightly-coupled
design that prevents code reuse. In addition, you might also create dependencies among
modules and applications that cause class sizes to be bigger. For more information, see
“Accessing modules from the parent application” on page 1129, “Accessing the parent
application from the modules” on page 1132, and “Accessing modules from other
modules” on page 1134.
■ Query string parameters — Modules are loaded with a URL; you can pass parameters on
this URL and then parse those parameters in the module. For more information, see
“Passing data with the query string” on page 1136.
[Bindable]
public var answer:Number = 0;
<mx:Form>
<mx:FormHeading label="Enter values to sum."/>
<mx:FormItem label="First Number">
<mx:TextInput id="ti1" width="50"/>
</mx:FormItem>
<mx:FormItem label="Second Number">
<mx:TextInput id="ti2" width="50"/>
</mx:FormItem>
<mx:FormItem label="Result">
[Bindable]
private var expenses:ArrayCollection;
</mx:Module>
[Bindable]
public var expenses:ArrayCollection = new ArrayCollection([
{Month:"Jan", Profit:2000, Expenses:1500},
{Month:"Feb", Profit:1000, Expenses:200},
{Month:"Mar", Profit:1500, Expenses:500}
]);
]]></mx:Script>
<mx:ModuleLoader url="ChartChildModule.swf" id="m1"/>
</mx:Application>
You can also call methods and access properties on other modules. For more information, see
“Accessing modules from other modules” on page 1134.
The drawbacks to this approach is that it can create dependencies on the parent application
inside the module. In addition, the modules are no longer portable across multiple
applications unless you ensure that you replicate the behavior of the applications.
To avoid these drawbacks, you should use interfaces that secure a contract between the
application and its modules. This contract defines the methods and properties that you can
access. Having an interface lets you reuse the application and modules as long as you keep the
interface updated. For more information, see “Using interfaces for module communication”
on page 1139.
]]></mx:Script>
<mx:ModuleLoader url="InterModule1.swf" id="m1"/>
<mx:ModuleLoader url="InterModule2.swf" id="m2"/>
</mx:Application>
Module 1:
<?xml version="1.0"?>
<!-- modules/InterModule1.mxml -->
<mx:Module xmlns:mx="http://www.adobe.com/2006/mxml" width="100%"
height="100%">
<mx:Script><![CDATA[
// Defines the method that the other module calls.
public function getNewTitle():String {
return "New Module Title";
}
]]></mx:Script>
</mx:Module>
Module 2:
<?xml version="1.0"?>
<!-- modules/InterModule2.mxml -->
<mx:Module xmlns:mx="http://www.adobe.com/2006/mxml" width="100%"
height="100%">
<mx:Script><![CDATA[
[Bindable]
private var title:String;
]]></mx:Script>
<mx:HBox>
<mx:Label id="l1" text="Title: "/>
<mx:Label id="myTitle" text="{title}"/>
</mx:HBox>
<mx:Button id="b1" label="Change Title" click="changeTitle()"/>
</mx:Module>
]]></mx:Script>
<mx:Form>
<mx:FormItem id="fi1" label="First Name:">
<mx:TextInput id="ti1"/>
</mx:FormItem>
<mx:FormItem id="fi2" label="Last Name:">
<mx:TextInput id="ti2"/>
</mx:FormItem>
</mx:Form>
<mx:ModuleLoader id="m1"/>
</mx:Application>
[Bindable]
private var salutation:String;
[Bindable]
public var selectedItem:Object;
[Bindable]
public var currentModuleName:String;
<mx:Form>
<mx:FormItem label="Current Module:">
<mx:Label id="l1" text="{currentModuleName}"/>
</mx:FormItem>
<mx:FormItem label="Adjuster ID:">
<mx:TextInput id="myId" text="Enter your ID"/>
function getModuleName():String;
function setAdjusterID(s:String):void;
function setBackgroundColor(n:Number):void;
}
<mx:Panel id="p1"
title="Auto Insurance"
width="100%"
height="100%"
backgroundColor="{bgcolor}"
>
<mx:Label id="myLabel" text="ID: {adjuster}"/>
</mx:Panel>
<mx:Script>
<![CDATA[
[Bindable]
private var adjuster:String;
[Bindable]
private var bgcolor:Number;