So I'll be doing more Flex development in my new position (and will also be touching CF a bunch more as an aside), and I decided to take another look at Swiz. I'm not a big frameworks guy, but in Flex there is a definite need to mediate events which I know Swiz does well, so I decided to take a peek.
I decided to hit a few things at once with my first Swiz application, a sort of technical spike for the project work I will be doing. For this example I needed to map relationships between entities. I found Mark Shepherd's
SpringGraph component which sounded like what I needed. It
"displays a graph of objects that are linked to each other, using a force-directed layout algorithm".
Perfect.
Next, I needed a persistence layer. I've gotten to play around with CouchDB a bit for prototyping, so I decided it would be great for this proof of concept. Why?
- Dynamic model
- Static queries (views)
- Restful interface
The latter is the most important to me here since, while the first two are important in the long run, a restful interface means I do not need a service layer to proxy my db requests. I've enjoyed prototyping in CouchDB previously and this got me up and running quickly. If you don't already have CouchDB installed and are on Windows, there is a
windows binary installer that works nicely. I named my database 'swizsample'. After you create the database you can copy
this file into your data directory to load the data for this example (Linux: /usr/local/var/lib/couchdb/ or Windows: %couch%\var\lib\couchdb\).
After installing
Flash Builder 4 (with the Flex 4.1 SDK) as a plugin to
eclipse, I grabbed my necessary libraries (included in the attached sample project):
After reviewing the Swiz
sample applications and reading the
documentation (not a ton of documentation is by design here) I created the following directory structure, simplistic since the app is a POC:
I then needed to tell Swiz where my application resources are located. In my Main.mxml file I added a few namespaces to my Application tag:
- xmlns:view="com.nictunney.view.*" - So I can import my base view
- xmlns:config="com.nictunney.config.*" - So I can tell Swiz where my config file is located
- xmlns:swiz="http://swiz.swizframework.org" - namespace for the Swiz framework
I then declared my Swiz configuration in Main.mxml using the swiz component tags beanProviders and config:
<fx:Declarations>
<swiz:Swiz>
<!-- BeanProviders simply contain the non-display objects that Swiz should process. -->
<swiz:beanProviders>
<config:Beans />
</swiz:beanProviders>
<swiz:config>
<!-- The eventPackages value tells Swiz the path to your Event classes,
and viewPackages is an optional value that speeds up the processing of display classes. -->
<swiz:SwizConfig
eventPackages="com.nictunney.event.*"
viewPackages="com.nictunney.view.*" />
</swiz:config>
</swiz:Swiz>
As you can see from the comments, I point beanProviders to my Beans.mxml configuration file. In this simple example I can put all of my bean configuration data in a single file, but you may have more depending on your architecture.
<swiz:BeanProvider
xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:swiz="http://swiz.swizframework.org"
xmlns:model="com.nictunney.model.*"
xmlns:control="com.nictunney.control.*"
xmlns:service="com.nictunney.service.*">
<service:MapService id="mapService" />
<control:MapController id="mapController" />
</swiz:BeanProvider>
As you can see above, I included my service and controller components for my map object (named map since we will be drawing a relationship map). Take a peek in the MapService.as class and you will see loadItems(), which makes an HTTP call to the CouchDB restful interface to retrieve a view (no queries, only document views in CouchDB). The successful result will call the httpResult method in the same class (more on that logic later).
The aptly named MapController.as class serves as a controller interface for the map service. Note that it makes use of the Swiz [Inject] metadata tag. This is clutch as the [Inject] metadata tag is performing a dependency injection by type here (as recommended by the Swiz docs) based on the definition in the Beans.mxml file. We can now reference the current state of the MapService using the mapService pointer (as seen on line 17).
I defined a single custom event for the application. Nothing to note here except that when calling the constructor on the Event superclass, you need to set the 'bubbles' property to 'true' (see line 13 in MapEvent.as). To hold the data there is a single GenericItem class defined. No real magic going on here either.
So, aside from the dependency injection, where else does Swiz get involved in the app? Glad you asked. Again, I feel that event mediation is the primary reason to use a framework in Flex. Code in Flex is pretty self organizing, but events can be a bear across a complex model with multiple views. Swiz handles event mediation well. If you take a peek at Map.mxml (the only view defined for this sample application), aside from the view and function to handle the SpringGrpah itself, there are two things of note handled by Swiz.
First, event mediation really is as simple as the following code:
[Mediate( event="MapEvent.PLACE_ITEM_REQUESTED", properties="item" )]
public function newItem(item:GenericItem): void {
trace('[Swiz] Mediating MapEvent.PLACE_ITEM_REQUESTED for item.' + item.id + '.');
var i: Item = new Item(item.id);
i.data = item;
g.add(i);
if(prevItem != null)
g.link(i, prevItem);
prevItem = i;
s.dataProvider = g;
}
The [Mediate] metadata tag tells Swiz to call the newItem() function when the MapEvent.PLACE_ITEM_REQUESTED event is fired anywhere in our example application. Note that we can use MapEvent directly (and not the package name) since we defined the eventPackages attribute in our swiz config in Main.mxml. Another important point here is the properties property. By telling Swiz to pass in the item property from our event, newItem() can now be called explicitly from elsewhere in our code (see that no event is being passed into the function itself?
The original event is fired from the MapService class when the HTTP call is completed. Two things to note in MapService.as. Since it is not a UI component we need to create an instance of IEventDispatcher and let Swiz know to monitor events passed from it by specifying the [Dispatcher] metadata tag:
[Dispatcher] public var dispatcher : IEventDispatcher;
This dispatcher is then used to send the events from our service. If we were dispatching an event from a UI component, Swiz would monitor it by default.
The last thing Swiz needs to know is where to start processing our application. For our needs we are telling Swiz to call the main() function in Map.mxml by using the [PostConstruct] metadata tag. [PostConstruct] is called after a display object is placed on the stage. Be sure to check out the
Swiz Bean lifecycle management page for more information.
If you run the application in debug mode (make sure you have the
debug Flash player) you'll see Flex load the SpringGraph instance onto the stage, Swiz call the main() function, triggering the MapEvent to fire, and newItems() receive the GenericItem objects and add them into the SpringGraph. Pretty sweet!
Source code for this example can be found
here