Category Archives: Flex

Using RobotLegs to Create a Widget for the ArcGIS Viewer for Flex

I submitted two talks to last week’s ESRI Developers Summit, one of which was not selected.  This made me indignant enough to write a blog post explaining the shunned presentation, in the hopes that all the people that did not vote for it will slap their foreheads and pray for the invention of time travel.  Or, maybe I just want to dialog on using Robotlegs with the Flex Viewer.

In the Advanced Flex presentation that Bjorn and Mansour gave at DevSummit, he extolled the use of frameworks.  Paraphrasing, he said “Find a framework and stick with it”  By “framework”, he meant something like PureMVC, Cairngorm (3, mind you, not 2), or Robotlegs.  I agree with the sentiment, wholly, although I am also a bit baffled at why the Flex Viewer didn’t use one.  My guess is that they will claim they have their own framework, which is certainly true, but it flies in the face of what Mansour spoke of in his presentation.  I raised my hand to ask this question, but (thankfully) time ran out on the questions and I was not selected.  It is probably just as well, as I don’t want to get on Mansour’s or Bjorn’s bad side.  They’ve both been very helpful to me in the past and I doubt I am even close to beyond needing more of their knowledge.

So, in this blog post, I am going to create a Flex Viewer Widget with Robotlegs.  First, though, I’ll explain a couple of things.  The reason I like using Robotlegs (or any well-supported framework) is myriad. It makes my widget much more testable, meaning, I can more easily write unit tests to exercise the widget.  This allows me to design the widget in a test-driven fashion, which is a good thing.  I wonder why the Flex Viewer source doesn’t include any unit tests.  Again, it’s not like ESRI is promoting bad habits, but they aren’t promoting good ones either.  Also, Robotlegs is known outside the ArcGIS world, so future Flex developers on my project that know Robotlegs will be able to get productive more quickly.  Granted, they’ll still have to learn about the ArcGIS API for Flex as well as the widget-based approach taken by the Flex Viewer, but the patterns Robotlegs uses brings context to this learning.  That’s my story, and I am sticking to it.

So, why Robotlegs?  Because I like it.  It’s got a great community and it makes a ton of sense.  Read on to see what I mean.

OK, let’s get to the code.

The Scenario

The widget we are building today is a Geolocation Widget.  (final product here) It’s function is to figure out where the user is and store that location in application, allowing the user to zoom to their location.  It will rely on javascript to do the IP Location search (which we won’t get into too much.  Suffice it to say that we’re using the HTML 5 Geolocation API for this with a fallback)

Now, I don’t really want this post to be an intro to Robotlegs.  There are plenty of those, including a very recent one by Joel Hooks (an RL founding member and crazy-smart guy) Read through those posts (as of this post, there are 3, with more on the way)  It should get you enough background to understand what is going on here.  Go ahead, I’ll wait.

Back? Confused? Robotlegs takes a bit to comprehend (not even sure if I totally get it, ahem) So, here are the things you’ll need to go through this in your own Flex development environment:

  • The ArcGIS API for Flex (I am using 2.2)
  • Robotlegs (using 1.4 here)
  • FlexUnit (4.0.0.2) and related swcs
  • AsMock (1.0) with the FlexUnit integration swc
  • Robotlegs Modular Utilities (github) Stray  (who is brilliant) and Mr. Hooks have written a modular utility for RL, and since our Flex Viewer is modular, we are using it.

For simplicity, just grab the git repository here, which has all this stuff set up for you.  Don’t get too hung up on the number of libraries and third party code.  This is how separation of concerns looks, leveraging the best library for a specific function.

The ‘Main’ Context

For those of you that did go read the Robotlegs introductory articles, you know that we have Contexts, Views, Mediators, Commands, and Services.  The context is the first thing we need to sort out, and there is a couple of caveats to our particular situation.  First, the Flex Viewer is not a RL application and, therefore, it does not have a context.  However, we need one, even using the Modular Utility.  In a nutshell, the Application Context is there as a parent context, and will supply the injector to the module contexts (unless you create one explicitly).  So, first thing to do is create the main context:

import com.esri.viewer.BaseWidget;
import flash.display.DisplayObjectContainer;
import flash.system.ApplicationDomain;
import org.robotlegs.core.IInjector;
import org.robotlegs.utilities.modular.mvcs.ModuleContext;
public class ApplicationContext extends ModuleContext {
	public override function startup():void {
		viewMap.mapType(BaseWidget);
	}
}

In the main application context, we register the BaseWidget class, which will allow Robotlegs to inject any widget going forward.  This makes using Robotlegs a bit more involved than just sticking with the “core” Flex Viewer widget approach, but it’s a small price to pay, in my opinion.

The View

Since functionality drives the widget, next is the view.  In this scenario. we want the geolocation to be loaded when the widget is first opened.  Once it is found, the location should be displayed and the user can pan to that location as desired.  In my mind’s eye, I see a couple of labels and a button.  Also, we have 3 states: Searching for location, location found, and location not found.  I see this:

We have a feedback area, the location is displayed, and the user can pan whenever they like with the “Go there!” button. Not gonna win any awards, but it gets the job done. From looking at our UI, there are a couple of takeaways: First, the location is loaded automatically, which means it happens when the user opens the widget. Second, we can start to think about our states and what is visible for each state. Finally, something has to handle user pressing that button. In RL, it’s the job of the Mediator to handle view events, so this is what ours looks like:

public class GeolocationWidgetMediator extends ModuleMediator
{
  [Inject]
  public var location:IGeolocation;
	
  [Inject]
  public var widget:GeolocationWidget;
	
  override public function onRegister():void{
    eventMap.mapListener(widget.btnGoThere, MouseEvent.CLICK, handleGoThere);
    //Could also use this, but then you have to figure out the target.
    //this.addViewListener(MouseEvent.CLICK, handleGoThere);
    this.addContextListener(GeolocationEvent.LOCATION_FOUND, handleLocationFound);
    widget.currentState = GeolocationWidget.STATE_SEARCHING_FOR_LOCATION;
    getLocation();
  }
  private function handleGoThere(event:MouseEvent):void{ 
    var point:MapPoint = com.esri.ags.utils.WebMercatorUtil.geographicToWebMercator(new MapPoint(location.x,location.y)) as MapPoint;
    widget.map.centerAt(point);
    addGraphic(point);
  }
  private function getLocation():void{
    //Find yourself
    dispatch(new GeolocationEvent(GeolocationEvent.GET_LOCATION));
  }
}	

When the Mediator is registered, we map the button click event to handleGoThere(), which simply zooms to our location. But wait, where did that location come from?

The Model

The location object is our Model and is of type IGeolocation (I used an interface for the model here, and now I kinda wish I hadn’t…oh well) We know it needs x/y coordinates, so we’ll make a model object that fulfills our requirement:

public class Geolocation implements IGeolocation
{
  private var _x:Number;
  private var _y:Number;
                  
  public function get x():Number{
    return _x;
  }
  public function get y():Number{
    return _y;
  }
  public function Geolocation(x:Number=0.00,y:Number=0.00)
  {
    _x = x;
    _y = y;
  }
}

There, now we have a model that is injected into our mediator. The model is a singleton (not a Singleton, it’s managed by RL) so that anything that cares about changes to the model can just have it injected and they’re always up to date. MMMM….that feels good. When we look at our module context, you’ll see how to make the model a singleton (we do it with services too!) For now, we’re still talking about the mediator. The getLocation() method fires off a GeolocationEvent, which we need to ‘splain.

Events

The Mediator takes view/framework events and translates them into Business Events. In this case, the loading of the widget is translated into a go-get-our-geolocation event, which we call GeolocationEvent.GET_LOCATION.

public class GeolocationEvent extends Event
{
  public static const LOCATION_FOUND:String = "locationFound";
  public static const LOCATION_NOT_FOUND:String = "locationNotFound";
  public static const GET_LOCATION:String = "getGeolocation";
  public var location:IGeolocation;
                  
  public function GeolocationEvent(type:String)
  {
    super(type);
  }
  override public function clone():Event{
    return new GeolocationEvent(type);
  }
}

Nothing special about events in RL, which is GOOD. Our GeolocationEvent has a payload of our location model, and it handles a few event types. We know when getGeolocation happens, the other two events are in response to finding out geolocation. We’ll talk a bit about those when we get to the service. For now, something has to respond to our GeolocationEvent.GET_LOCATION

Commands

In RL, you handle business events with Commands. Commands are responsible for answering the question raised by the event, either by going outside the application (when they talk to services) or by some other means. In our case, we’ll make a GeolocationCommand that calls some service to get our geolocation.

public class GetGeolocationCommand extends Command
{
  [Inject]
  public var service:IGeolocationService;

  [Inject]
  public var event:GeolocationEvent;
                  
  override public function execute():void{
    service.getGeolocation();
  }
}

About the simplest command you can possibly have. Also, note how RL is helping us flush out our needs while guiding us in using best practices. What have you done for RL lately? Yet it still guides you on the path of rightgeousness. Let’s discuss that IGeolocationService.

Services

The ‘S’ in MVCS. Services are our gateway to what lies beyond. When we need to go outside the app for data, we ask a service to do that. In this case, our service is going to ask the HTML5 Geolocation API for our current location. This means, basically, that we call out using ExternalInterface and define a couple of callbacks so we can handle whatever the service returns.

public class GeolocationService extends Actor implements IGeolocationService
{
  public function GeolocationService()
  {
    ExternalInterface.addCallback("handleLocationFound",handleLocationFound);
    ExternalInterface.addCallback("handleLocationNotFound",handleLocationNotFound);
  } 
  public function getGeolocation():void
  {
    ExternalInterface.call("esiGeo.getGeolocation");
  }
  public function handleLocationFound(x:Number, y:Number):void{
    var location:Geolocation = new Geolocation(x,y);
    var event:GeolocationEvent = new GeolocationEvent(GeolocationEvent.LOCATION_FOUND);
    event.location = location;
    dispatch(event);
  }
                  
  public function handleLocationNotFound():void{
    var event:GeolocationEvent = new GeolocationEvent(GeolocationEvent.LOCATION_NOT_FOUND);
    dispatch(event);
  }
}

Notice that our service extends the Actor class, which is an RL class that (basically) gives us access to EventDispatcher. The service will fire events (as you see above) when it gets back results from the beyond.

Something has to handle the events thrown by the service. We want these results to show up in our view, which means, we want these results to change our model. Back to the Mediator.

The Circle of RL

Back in the GelocationWidgetMediator, we can subscribe to the events raised by the service. As a side note, you really have a couple of options here. If your data is more complex than an x/y pair, you’ll likely want to parse it before it gets to the mediator. At this point, I would recommend you look at Joel’s posts and, for something super cool and useful, the Robotlegs Oil extensions. I will likely blog about Oil in the future. You could also create a presentaion model, inject it into the view and data bind to the model objects on the PM. I am doing it the simplest way because my scenario is simple. Anyway, back to the mediator and handling the events raised by the service. You subscribe to context events in the same onRegister() function that you subscribe to view events.

override public function onRegister():void{
  eventMap.mapListener(widget.btnGoThere, MouseEvent.CLICK, handleGoThere);
  //Could also use this, but then you have to figure out the target.
  //this.addViewListener(MouseEvent.CLICK, handleGoThere);
  this.addContextListener(GeolocationEvent.LOCATION_FOUND, handleLocationFound);
  this.addContextListener(GeolocationEvent.LOCATION_NOT_FOUND, handleLocationNotFound);

  widget.currentState = GeolocationWidget.STATE_SEARCHING_FOR_LOCATION;
  getLocation();
}

public function handleLocationNotFound(event:GeolocationEvent):void{
  widget.currentState = GeolocationWidget.STATE_LOCATION_NOT_FOUND;
}

We saw handleLocationFound() above, here I added the handling of the not found scenario. All it does is set the state to the “not found” state, which hides buttons/labels/whatever.

The Context

As I mentioned earlier, the module needs its own module context. The context’s job is to wire everyting up. Our mediator is mapped to its view, events to commands, and the services our registered. Also, the items that will be injected to the various players (like the model and the service) are specified.

public class GeolocationWidgetContext extends ModuleContext
{
  public function GeolocationWidgetContext(contextView:DisplayObjectContainer, injector:IInjector){
    super(contextView, true, injector, ApplicationDomain.currentDomain);
  }

  override public function startup():void{
    //Singletons
    injector.mapSingletonOf(IGeolocationService, GeolocationService);
    injector.mapSingletonOf(IGeolocation,Geolocation);
                            
    //Mediators
    mediatorMap.mapView(GeolocationWidget, GeolocationWidgetMediator);

    //Commands
    commandMap.mapEvent(GeolocationEvent.GET_LOCATION, GetGeolocationCommand,GeolocationEvent);
    commandMap.mapEvent(GeolocationEvent.LOCATION_FOUND, FindPolygonCommand, GeolocationEvent);
  }
}

Our context extends ModuleContext, which is supplied by the Robotlegs Modular Utilities. The ModuleContext creates a ModuleEventDispathcher and ModuleCommandMap, and is basically (as Joel states here) just a convenience mechanism. Since we are in a module, there is one last little item we have to do to make this all come together. Our module should (read: needs to) implement the org.robotlegs.utilities.modular.core.IModule interface. This defines two functions (a setter for parentInjector and a dispose() method) that ensures the API to initialize the module and RL is in place. So, in the script of the MXML, you have:

[Inject]
public function set parentInjector(value:IInjector):void{
  context = new GeolocationWidgetContext(this,value);
}
//Cleanup the context
public function	dispose():void{
  context.dispose();
  context=null;
}

The set parentInjector allows us to create a child injector as well as use mappings from the main context. Read Joel’s post on Modular stuff for more detail. The dispose() function is just good practice, allowing you to free up anything you need to free up. The mapSingletonOf calls are how you tell RL to just make one of these things. Above the Geolocation model object is made a singleton, so the mediator and the command get the same copy. In a more complex widget, you could data bind to that bad boy and anything that changes it shows up in the view without any code. That…howyousay?…rocks!

That really covers the meat of creating a Flex Widget for the ArcGIS Viewer for Flex using Robotlegs. As I mentioned, this was submitted to the Flex-a-Widget challenge at the Developers Summit and did not place. The winners (which, to be honest, I voted for) did things display Street View and Bing 3D and windows into your friggin’ soul in the Flex Viewer, so you can see why this simple-to-the-point-of-being-useless widget did not place. Still the guts of this widget are pretty sexy, and now hopefully you can build your own soul-displaying widgets using Robotlegs.

The code on github has a few items that I didn’t think pertinent to this blog, like unit tests (which are VERY important and a BIG reason why using something like RL is crucial) Also, the source has another sequence of taking the point and finding the county where the user is currently, just for fun. I hope you found this useful. If I messed anything up or got something wrong, please let me know in the comments. Much of the reason I do blog posts like this one is to confirm that things are what I think the are. I have (frequently) been wrong before, so correct me if you see an error.


Robotlegs and Cairngorm 3: Initial Impressions

Smackdown? (Well, not really...)

I have had Robotlegs on my radar screen for months now, just didn’t have the time/brains to really check it out.  I cut my Flex framework teeth on Cairngorm 2, which seems to be the framework non grata of all the current well-known Flex frameworks (Swiz, PureMVC, Robotlegs, etc.)  However, at Joel Hooks‘ suggestion, I took a look at Cairngorm 3 (henceforth referred to as CG3), enough to do a presentation on it for a recent nerd conference. The presentation includes a demo app that I wrote using Cairngorm 3, which (currently) uses Parsley as it’s IoC container.  That’s right, Cairngorm 3 presumes an IoC container, but does not specify one.  This means that you can adapt the CG3 guidelines to your IoC of choice.  This is only one of the many ways CG3 is different from CG2, with others being:

  • No more locators, model or otherwise.  Your model can be injected by the IoC or handled via events.  For you Singleton haters, this is a biggie.
  • A Presentation Model approach is prescribed, meaning your MXML components are ignorant of everything except an injected presentation model.  The views events (clicks, drags, etc) call functions on the presentation model inline.  The presentation model then raises the business events.  This allows simple unit testing of the view logic in the presentation models.
  • The Command Pattern is still in use in CG3, but commands are not mapped the same way.  For CG3, your commands are mapped to events by your IoC.  Parsley has a command pattern approach in the latest version that actually came from the CG3 beta.  This approach uses metadata (like [Command] and [CommandResult]) to tell Parsley which event to map.  Again, this results in highly testable command logic.
  • CG3 includes a few peripheral libraries to handle common needs that are very nice.  Things like Popups, navigation, validation, and the observer pattern are all included in libraries that are not dependent on using CG3 or anything, really.  If you don’t intend to use CG3, it may be worth your while just to check out these swcs.

All in all, CG3 is a breath of fresh air for someone who has been using CG2.  Cairngorm 2 is fine, and it’s certainly better than not using any framework, but the singletons always made me uneasy and, in some of our lazier moments, we ended up with hacks to get the job done.  I feel that CG3 really supports a test-driven approach and Parsley is very nice and well-documented.   It’s worth saying that I only know enough about Parsley to get it to work in the CG3-prescribed manner, and there seems to be much more to the framework.

Once I had a basic handle on CG3, Joel said he was interested in how CG3 could work with Robotlegs (henceforth referred to as RL).  Also, after my presentation, a couple of folks wandered up and mentioned RL as well.  So, when I got home, I ported the demo app to RL (it’s a branch of the github repo I link to above) so I could finally check it off my Nerd Bucket List.

First of all, Robotlegs is, like CG3, prescriptive in nature.  It is there to help you wire up your dependencies and maintain loose coupling to create highly testable apps (right, all buzzwords out of the way in one sentence.  Excellent)  Like CG3, it presumes an IoC, but does not specify a particular one.  The “default” implementation uses SwiftSuspenders (link?) but it allows anyone to use another IoC if they feel the need.  I have heard rumblings of a Parsley implementation of RL, which I’d like to see.  Also, the default implementation is more MVCS-ish than the default CG3 implementation.  What the hell does that mean?  Well, MVC is a broad brush and can be applied to many architectures.  In my opinion, the CG3-Parsley approach uses the Presentation Model as the View, the Parsley IoC/Messaging framework for the controller and injected value objects for the model.  The RL approach uses view mediators and views for the view, which reverses the dependency from CG3.    The RL controller is very similar, but commands are mapped explicitly in the context to their events, rather than using metadata.  The model is also value objects, but it’s handled differently.  In CG3, the model is injected into commands and presentation models, then bound to the view.  So, if a command changes a model object, it’s reflected in the presentation model, and the view is bound to that attribute on the presentation model. In RL, the command would raise an event to signify model changes, passing the changes as the event payload.  The view mediator listens for the event, handles the event payload to update the view, again through data binding. (NOTE:  You can handle the model this way in Parsley as well, using [MessageHandler] metadata tags, FYI)

It’s worth mentioning that when I did the RL port, I added a twist by using the very impressive as3-signals library from Robert Penner.  Signals is an alternative to event handling in Flex, and I really like it.  Check it out.  Anyway, RL and Signals play very well together, but it means I wasn’t necessarily comparing apples-to-apples.  Signals is not a requirement of using RL, at all, but the Twittersphere was raving about it and I can see why.  The biggest con to using Signals with CG3 might be some of the peripheral CG3 libraries.  For example, I think you’d end up writing more code to adapt things like the Navigation Library to Signals.  The Navigation Library uses NavigationEvent to navigate between views, which would need to be adapted to Signals.  Of course, I am of the opinion that, if you are going to use something like Signals, you should use it for ALL events and not mix the two eventing approaches.  This is a philosophical issue that hasn’t had the chance (in my case) to be tested by pragmatism.

So, which framework do I like better?  After a single demo application, it’s hard to make a firm choice.  I really like the CG3 approach, and it’s only in beta.  However, I also really like the Signals and RL integration, which I think makes eventing in Flex much easier to code and test.  I am not that big a fan of the SwftSuspenders IoC context, as there doesn’t seem to be anyway to change parameters at runtime, which is something I use IoC to handle.  An example is a service URL that would be different for test and production.  I’d like to be able to change that in an XML file without having to rebuild my SWF.  I asked about this on the Robotlegs forum, and was told that it’s a roll-your-own answer.  On the other hand, Parsely offers the ability to  create the context in MXML, actionscript, XML, or any mixture of the three.  I like that.  I think the winner could be a Parsley-RL-Signals implementation pulling in the peripheral CG3 libraries, which mixes everything I like into one delicious approach.  MMMMMM.

Anyone have questions about the two frameworks that I didn’t cover?  Hit the comments.  Also, if anything I have said/presumed/implied here is flat out wrong, please correct me.  The last thing I want to do is lead people (including myself) down the wrong path.

Related articles by Zemanta (well, some of them were by Zemanta, others by me)

People You Should Follow on Twitter About This Stuff

Reblog this post [with Zemanta]

Continuous Integration with Flex, Hudson, and ArcGIS Server-Part V

(Part 1, Part 2, Part 3,Part 3, Part 4)

In what I hope is the last post in this particular series, we will get Hudson up, building our project and running our tests. Let’s get to it.

Get Hudson

Download Hudson from here. This will download hudson.war, which is a java web archive. Oh, and I presume that you have a java SDK installed somewhere. If you don’t go to java.com and get one.

Start Hudson

Hudson is super easy to get up and running. I suggest you copy the hudson.war file to c:\hudson and open a command prompt to that directory. Type:

java -jar hudson.war

This will start the Hudson service on port 8080. If you have anything already running on this port (I did) you need to shut it down or pass the –httpPort= to the command with the port you wish to use.

Talk to Hudson

Open up a browser to http://localhost:8080 and you’ll see this:
Hudson Dashboard

We need to create a job for our Flex project. Click New Job put in a job name (I am using “AGSFlexBuild”) and select “Build a free-style software project” and Click OK. This will bring up the main configuration of the build. The first item we will set up is the Subversion repository. The high-level process that Hudson will follow is:

  1. Poll Subversion for changes
  2. When a change is detected, run an SVN updte to get the latest version
  3. Build the project
  4. On a successful build, run the tests

So, the first thing we should do is setup our Subversion repository information.

Hudson SVN

Put your repository URL in as shown in the above image. In the “Build Triggers” section select “Poll SCM” and put “* * * * *” in the “Schedule” text box. This will cause Hudson to poll SVN every minute.

Now we have to add the build step. Since we have already set up our ant build script, this part is easy. Click “Add Build Step” and select “Invoke Ant”, then fill out the text box as shown here:
Hudson Build Step

At this point if we ran the build from Hudson, we’d get an error complaining about ${deploy_dir}. The reason for this is we don’t have a local.properties file on the server (remember when we did that?). There are several possible solutions to this issue.

  • Create a ci.properties file, check it in to SVN, and pass a parameter to the build step in Hudson.
  • Create an ant task that copies the local.properties.template file to local.properties, then add a build step in Hudson that runs before the build-flex-project step.
  • Default the properties in the build.xml file.

The last option is the ickiest, but the first two are about the same, in my opinion. I took the second option, creating an ant task and adding it to my Hudson configuration.
Hudson Build Step 2
I leave the creation of the ant task as an exercise for the reader.

The final build step to be configured will run the tests. Add another build “Invoke Ant” build step that invokes the “test” ant task. At the bottom of the page, click the “Save” button. This will take you to the AGSFlexBuild Project page.

Run the build

Click “Build Now” on the left hand side of the page and the build will be scheduled. The build should run successfully. Click on the hyperlink with the build date and time, which will take you to the details of the build. If you click on “Console Output”, the entire build log is displayed. When things go awry in the build, you’ll likely go here first.

A continuous integration process is incomplete without some kind of notification when the build fails. There are a couple of options for notification: e-mail, CCTray, or twitter, just to name a few. E-mail is available in the base Hudson package (see Manage Hudson, Configure System, put in SMTP information). CCTray allegedly works, but I don’t use it and can’t find it. Twitter is available as a Hudson plugin.

Speaking of plugins, there are TONS of them. Twitter, Git, Windows Authentication, etc. You can find them in the Hudson configration pages of the site. I reccommend you look through them and find what you need.  Finally, it’s super easy to get Hudson running as a Windows service, as there is a “Install as Windows Service” option in the Manage Hudson configuration area.

It’s difficult to find a stopping place when discussing Hudson, so if you have questions, send me an e-mail or leave a comment. The code for this series can be found on github (you could fork it and test using Hudson with git, just for fun…. ;)) I hope someone found this series useful.


Continuous Integration with Flex, Hudson, and ArcGIS Server, Part IV

(Part 1, Part 2, Part 3, Part 5)

(Note:  I have started moving our builds to rake, the Ruby build DSL.  It is, howyousay, fantastic.  I see more posts in our series….)

Now that the school year has begun, I can get back into this series.  Last we left, we had ant running the build and the scripts.  Now it’s time to make the build portable, which means that our build.properties file needs to well, go away.  All this really means is taking build.properties and copying it to build.properties.template.  Then deleting build.properties out of source control.  Anyone getting our build our of source control will have to create a local build.properties file, complete with all the settings that make the build go.  This is a “best practice” and stops developers from overwriting each others’ properties when they check in the build file.

So, do that.  Rename build.properties to build.properties.template.  If you ant build-all now, the build throws an error, something about ${wrapper.dir} not found.  That’s because the build couldn’t pull in the properties file.  Copying build.properties.template to build.properties fixes the build.  However, we aren’t done with the renaming.  I want to introduce the concept of build environments to my build.  That way, I can have different properties files for named environments.  I may want to set up different SDK builds, etc. as well, and environments will help me do that.  All I need to do is add a property to the top of the build.xml file to hold my environment name.  It looks like:

<property name="env" value="local"/>

This means that, by default, I’ll be running in the local environment.  This also means that build.properties.template becomes local.properties.template.  Now, I have to copy local.properties.template to local.properties to get the build going.  Another change to the build.xml file is to the line that pulls in our properties file.  It changes from:

<property file='build.properties'/>
to
<property file='${env}.properties'/>

The cool thing is that I can add another file called (for example) 34SDK.properties that points to the Flex 3.4 SDK.  Then to run that build, I can write:

ant build-all -Denv=34SDK

and it will overwrite our env variable and look for a 34SDK.properties file.  Flexibility is fun.

The SCMelephant in the Room

Up to this point, I have committed a cardinal sin.  No source control.  I know, bad developer.   However, I want to get the directory structure set up in a more CI friendly manner before I involve an SCM, so there is a method to the madness.  Let’s do that now.  There are roughly 1, 233, 443 articles on a developer’s workspace or CI directory structure.  I am going to use one that I’ve borrowed (and slightly modified) from Vish.  He is smarter than me, so it must be the way to go.  Here is the structure, in pictures:

3479c0f09a281e0fc8655d29fcba313b

You can ignore the .metadata folder, as that is a Flex Builder thing and I won’t put it in source control.   The build folder will hold the output of our build, docs is self-explanatory, src is where our Flex Builder project lives, and tools holds stuff we need to run the build.  In this case, we need the FluintAirTestRunner.exe, which I’ll copy into my tools dir.  The build file lives in the root of our project space, which means it has moved up relative to the Flex Builder project.  We need to change some things in the build.properties file to get our build going again.

#Copy this file locally to local.properties and change below for your env

# change this to your Flex SDK directory path
FLEX_HOME=C:/Program Files (x86)/Adobe/Flex Builder 3/sdks/3.2.0

# this points to your project's src directory
# {$basedir} is a default variable that can be used in any Antscript
# and it points to the project's root folder [ flex_ant_pt1_Tasks ]
# in this case
srcpath.dir =${basedir}/src/FlexAGSCI/src

# points to the project's libs directory
libs.dir =${basedir}/src/FlexAGSCI/libs

# this is the folder we want to publish the swf to
deploypath.dir = ${basedir}/build

wrapper.dir=${basedir}/src/FlexAGSCI/html-template

version.major =0
version.minor=9
version.revision = 0
APP_TITLE = Flex Solution
APP_WIDTH = 100%
APP_HEIGHT =100%
locale = en_US
html.file=${deploypath.dir}/index.html
application.name=FlexAGSCI

#Fluint vars
fluint.dir = ${basedir}/tools/FluintAIRTestRunner
report.dir = ${deploypath.dir}/reports

Now, running ‘ant’ at the project space root builds the project, and running ant test builds the test module and runs the test.  We copy this file to local.properties.template, and we are ready to import this project into source control.

I found a couple of posts that show how to import an existing directory into Subversion and make that directory your working copy, which is snazzy. It’s all here.  If you know anything about svn, that is a pretty straightforward post.

That’s that.  We are now ready for Hudson.  in the next post, we’ll get Hudson up, running, building our stuff, and running our test.


Continuous Integration with Flex, Hudson, and ArcGIS Server, Part 2

(Part 1Part 3Part 4)

In the last post, I talked about the very basics of getting a Flex project going with Flex Builder and ant.  The current ant script we have just builds the SWF file, which leaves out a bunch of files that Flex Builder uses, and that is bad as defined by what I am trying to blog about here.  So, let’s get going on pulling over the rest of the files.  Well, at least, the important ones.  The most important one is likely the HTML file that wraps our SWF.  If we are going to deploy this SWF in any meaningful (read: web) manner, we need that HTML file.  Actually, we need one that we make ourselves and pull into our build process, but that will left as an assignment for the reader.  For the sake of this series, we’ll just reuse the HTML file that Flex Builder was kind enough to generate.

In the default Flex Builder project, one of the folders is html-template, that looks like:

image

Not much to it, really.  I am not gonna go into the meat of what the files are, if you want that go here.  In a nutshell, what we need to do here is copy this whole directory to our output folder (our ${deploy.dir} for you sharp ones following along).  There is a slight caveat here, tho, and that’s the contents of the index.template.html.  If you open that file in a text editor, you’ll see snippets like:

AC_FL_RunContent(
        "src", "playerProductInstall",
        "FlashVars", "MMredirectURL="+MMredirectURL+'&MMplayerType='+MMPlayerType+'&MMdoctitle='+MMdoctitle+"",
        "width", "${width}",
        "height", "${height}",
        "align", "middle",
        "id", "${application}",
        "quality", "high",
        "bgcolor", "${bgcolor}",
        "name", "${application}",
        "allowScriptAccess","sameDomain",
        "type", "application/x-shockwave-flash",
        "pluginspage", "http://www.adobe.com/go/getflashplayer"
    );

where you may notice the now familiar ${} variables.  So, now our simple copy has turned into a copy and replace all the ${} tokens with their appropriate values.  Nothing is ever as simple as it should be.

The good news is that it’s not so hard either.  Breaking down what we need to do, I think it looks something like this:

  1. Copy the html-template contents.
  2. Parse the index.template.html and replace the tokens.

Let’s create our ant task to get this done.  I’ll it create-html-wrapper, and it looks like this:

<target name='create-html-wrapper'>
    <copy todir='${deploypath.dir}' overwrite="true">
      <fileset dir='${wrapper.dir}'>
         <exclude name='**/index.template.html' />
     <exclude name='**/index.template.html.svntmp' />
         <exclude name='.svn' />
      </fileset>
</copy>

The copy ant task is a standard task from the ant library.  We are excluding some items here:  we don’t want any source control artifacts to end up in the deploy directory.  I am using subversion here, so I explicitly exclude those.  The one that may look odd is the exclusion of index.template.html.  The reason that file is excluded is because we want to rename it.  I don’t want my HTML file to be named index.template.html, do you?

(NOTE:  The Flex ant tasks include an html-wrapper task, but I found it lacking for reasons that now escape me.  I think the parsing of the HTML files was either not there or crappy.  Either way, I dumped it for some very valid reason, I am sure.)

For the index.template.html, we can use another cool ant ask that uses regular expressions to replace content within a file.  The name of that task is replaceregexp, and it’s dead sexy.  Here is the copy command, and it’s a bit of a doozy.  I am not gonna go through each token, as that would be REALLY boring (not super exciting like the rest of this post)

<!-- Copy and rename the index.template.html -->
<copy file='${wrapper.dir}/index.template.html'
tofile='${html.file}' />
<!-- Copy and rename the index.template.html --> <copy file='${wrapper.dir}/index.template.html' tofile='${html.file}' /> <replaceregexp file='${html.file}' flags='gs' match='\$\{width\}' replace='100%'/> <replaceregexp file='${html.file}' flags='gs' match='\$\{height\}' replace='100%'/> <replaceregexp file='${html.file}' flags='gs' match='\$\{title\}' replace='${APP_TITLE}' encoding='utf-8'/> <replaceregexp file='${html.file}' flags='gs' match='\$\{version_major\}' replace='${version.major}'/> <replaceregexp file='${html.file}' flags='gs' match='\$\{version_minor\}' replace='${version.minor}'/> <replaceregexp file='${html.file}' flags='gs' match='\$\{version_revision\}' replace='${version.revision}'/> <replaceregexp file='${html.file}' flags='gs' match='\$\{application\}' replace='${APP_TITLE}'/> <replaceregexp file='${html.file}' flags='gs' match='\$\{bgcolor\}' replace='#FFFFFF'/> <replaceregexp file='${html.file}' flags='gs' match='\$\{swf\}' replace='${application.name}'/>
(BTW, the replaceregexp stuff I blatantly stole from here, which is a GREAT post on using ant with Flex.  Also goes to show you that I am a couple of years behind the cool kids.)

Finally, let’s take another look at our build.properties file, which added quite a bit of properties for this task.

#Copy this file locally to build.properties and change below for your env

# change this to your Flex SDK directory path
FLEX_HOME=C:/Program Files (x86)/Adobe/Flex Builder 3/sdks/3.2.0

# this points to your project's src directory
# {$basedir} is a default variable that can be used in any Antscript
# and it points to the project's root folder [ flex_ant_pt1_Tasks ]
# in this case
srcpath.dir =${basedir}/src

# points to the project's libs directory
libs.dir =${basedir}/libs

# this is the folder we want to publish the swf to
deploypath.dir = ${basedir}/bin-debug

wrapper.dir=${basedir}/html-template

version.major =0
version.minor=9
version.revision = 0
APP_TITLE = Flex Solution
APP_WIDTH = 100%
APP_HEIGHT =100%
locale = en_US
html.file=${deploypath.dir}/index.html
application.name=FlexAGSCI

We have some of our HTML tokens, like width and height, a new directory (wrapper.dir) and the name of our HTML file.  For the ant build, I rename it to index.html because I plan on using ant to actually deploy the application to a web server and I want a default document name to be used.

So, next post will concentrate on adding a unit testing framework and getting ant to run our tests too.  I know the first couple of posts were mongo-basic, but I wanted to lay a bit of a foundation before getting into the cooler stuff.  We’ll get there.  I am about to head off on a mini-vacation, so it might be a week or more before I get to it.  I hope you can manage until then….

Oh, and here’s our build.xml, in full, just for fun:

<project name='Flex Ant Tasks Build Script' default='build-flex-project'>
    <!-- load properties file -->
    <property file='build.properties'/>

    <!-- points to are flexTasks.jar -->
    <taskdef resource='flexTasks.tasks' />    

    <!-- delete and redeploy -->
    <target name='init'>
        <delete includeemptydirs='true'>
            <fileset dir='bin-debug' includes='**\*' excludes='**\.svn'/>
        </delete>

    </target>
    <target name='build-flex-project' depends='init,create-html-wrapper'>
        <mxmlc file='${srcpath.dir}/${application.name}.mxml' output='${deploypath.dir}/${application.name}.swf'>
            <load-config filename='${FLEX_HOME}/frameworks/flex-config.xml'/>
            <include-libraries file='${libs.dir}' />
        </mxmlc>

    </target>

    <target name='create-html-wrapper'>
        <copy todir='${deploypath.dir}' overwrite="true">
              <fileset dir='${wrapper.dir}'>
               <exclude name='**/index.template.html' />
               <exclude name='**/index.template.html.svntmp' />
               <exclude name='.svn' />
              </fileset>
        </copy>
          <!-- Copy and rename the index.template.html -->
         <copy file='${wrapper.dir}/index.template.html'
              tofile='${html.file}' />
          <replaceregexp
              file='${html.file}'
              flags='gs'
              match='\$\{width\}'
              replace='100%'/>
           <replaceregexp
              file='${html.file}'
              flags='gs'
              match='\$\{height\}'
              replace='100%'/>
         <replaceregexp
              file='${html.file}'
              flags='gs'
              match='\$\{title\}'
              replace='${APP_TITLE}'
              encoding='utf-8'/>
         <replaceregexp
              file='${html.file}'
              flags='gs'
              match='\$\{version_major\}'
              replace='${version.major}'/>
         <replaceregexp
              file='${html.file}'
              flags='gs'
              match='\$\{version_minor\}'
              replace='${version.minor}'/>
           <replaceregexp
              file='${html.file}'
              flags='gs'
              match='\$\{version_revision\}'
              replace='${version.revision}'/>
         <replaceregexp
              file='${html.file}'
              flags='gs'
              match='\$\{application\}'
              replace='${APP_TITLE}'/>
          <replaceregexp
              file='${html.file}'
              flags='gs'
              match='\$\{bgcolor\}'
              replace='#FFFFFF'/>
         <replaceregexp
              file='${html.file}'
              flags='gs'
              match='\$\{swf\}'
              replace='${application.name}'/> 

    </target>
</project>

Continuous Integration with Flex, Hudson, and ArcGIS Server, Part 1

(Part 2, Part 3, Part 4)

I’ve been meaning to blog about our findings with getting CI going for Flex, since I took time to whinge about it earlier.  It was a bit of a journey, but all in all, not that bad.  Much of the thanks goes to Hudson, which is, how you say?, super-fantastic.  I plan to break this into a few blog posts rather than one big-old doozy.   The parts (I think) will be along the lines of:

  1. Getting your Fles project building with ant and Flex Builder, parts 1 and 2.
  2. Setting up unit testing with Fluint.
  3. Setting up Hudson.

Whachoo Need (OOH)

This post is focused on getting your Flex Builder project building with ant.  As such, I presume you are using Flex Builder, but if you aren’t and just want to build with ant, you should be OK. Oh, and I am doing this on Winders, even though ant is usually the domain of the Unix-based Java wanks (I mean “wanks” as a term of endearment, like “I love those wanks”)

Go get the following and download to your computer:

  • ANT – The original builder.  It can carry, like, 10,000 times its own weight in builds.  Unzip it to c:\ant.
  • Flex Ant Tasks – These come with the Flex SDK, which comes with Flex Builder.  You can find them in C:\Program Files (x86)\Adobe\Flex Builder 3\sdks\3.2.0\ant\lib\flexTasks.jar.  If you don’t have Flex Builder, go download the SDK and put it somewhere.
  • ArcGIS Server Flex API – So we can get our map on, know what I’m saying? (I am using 1.2)

Create the Project

Open Flex Builder and create a new project.  I am presuming that your local workspace is c:\projects\FlexCI, though it really doesn’t matter.

So, we’ll start by creating a new Flex Project in Flex Builder.

Create Flex Project

Hit ‘Next’ in the project wizard and we have our project.  Copy the AGS Flex API swc into your libs folder (see pic).

flexapi

Now we can make a map and stuff.  Add the following code to your app:

<esri:Map>
     <esri:ArcGISTiledMapServiceLayer
          url="http://server.arcgisonline.com/ArcGIS/rest/services/ESRI_StreetMap_World_2D/MapServer"/>
</esri:Map>

Now we have our map.  In Flex Builder, build (Ctrl+B, unless you are using ‘Build Automatically’) the site and run it, you should see a map of our world.  Building with Flex Builder works, which was a requirement for me.  I wanted people to be able to build and debug in the IDE, but also quickly build from the command line with ant.  I like Flexibility, #rimshot.

High Apple Pie in the Sky Hopes

In order to build with ant, we need a couple of items

  • A build file, we will call build.xml
  • A properties file, we will call build.properties

I am not about to go into the gory details of how ant works, but presume you know the basics, which is all I know.  We need to define a build target that compiles our Flex project.  The steps the build file will go through are:

  1. Clean out the build dir.
  2. Build the project.

In ant-speak, that is two targets.  I will call them ‘init’ and ‘build-flex-project’.  For the init target, we just need to delete everything in the directory that Flex Builder set up as the deploy location, which is (by default) the bin-debug directory.  Create a build.xml file in your Flex Project root, has the following XML:

<project name='Flex Ant Tasks Build Script' default=”init”'>
    <!-- delete and redeploy -->
    <target name='init'>
        <delete includeemptydirs='true'>
            <fileset dir='bin-debug' includes='**\*' />
        </delete>
    </target>
</project>

This is a fully-functional ant script now.  Test it by opening a command prompt, switching to the project directory, and typing ‘c:\ant\bin\ant’ (minus the quotes, of course)  You should see something like:

firstant

Also, your bin-debug directory should be empty.  WOOHOO!  If you build with Flex Builder again (you may have to “Clean” since FB won’t build unless it detects a change) the files will go back into the bin-debug dir.  Right, now let’s get the build part going.  Adobe was nice enough to create ant tasks that will build Flex projects.  The easiest way to make them available to your ant script is to:

  1. Copy the flexTasks.jar file into your c:\ant\lib folder.
  2. Reference these tasks in your build.xml file.

The second part is done by adding the following code to the top of the build.xml file:

<!-- points to are flexTasks.jar -->
<taskdef resource='flexTasks.tasks' />

That’ll do it.  (NOTE:  In a real CI scenario, which we’ll get to, we would not copy the jar into the ant\lib folder, but make it part of the project artifacts and reference it locally.  Bear with me, baby steps on the bus…)

Now, let’s add the build-flex-project target, which uses the mxmlc Flex ant task.

<target name='build-flex-project' depends='init'>
     <mxmlc file='${srcpath.dir}/${application.name}.mxml' output='${deploypath.dir}/${application.name}.swf'>
           <load-config filename='${FLEX_HOME}/frameworks/flex-config.xml'/>
           <include-libraries file='${libs.dir}' />
     </mxmlc>
</target>

WHOA!  OK, so I’ve jumped ahead a bit, so lemme ‘splain.  First off, we tell ant that this target depends on the init target, so it will run init before running the build.  Second, those funky ${} items that you see are properties.  So, you can see that I have one for”:

  • srcpath.dir – the source directory, so we knows what to build.
  • FLEX_HOME – this is where our SDK lives
  • deploypath.dir – where to put the build output
  • libs.dir – where are external libraries (like the AGS Flex API) live
  • application.name – What do we want our SWF to be called.

Now we need to put these properties somewhere, and the aforementioned build.properties file is where they belong.  Looks like:

#Copy this file locally to build.properties and change below for your env

# change this to your Flex SDK directory path
FLEX_HOME=C:/Program Files (x86)/Adobe/Flex Builder 3/sdks/3.2.0

# this points to your project's src directory
# {$basedir} is a default variable that can be used in any Antscript
# and it points to the project's root folder [ flex_ant_pt1_Tasks ]
# in this case
srcpath.dir =${basedir}/src

# points to the project's libs directory
libs.dir =${basedir}/libs

# this is the folder we want to publish the swf to
deploypath.dir = ${basedir}/bin-debug

application.name=FlexAGSCI

We need to tell our build.xml file about the build properties file, and we do so by adding this to the top (meaning, first thing under the <project> opening tag):

<!-- load properties file -->
<property file='build.properties'/>

The last thing we need before running the build again is to change the default target to build-flex-project.  You’ll find the default attribute on the root project element.

Now, running ‘c:\ant\bin\ant’ (btw, putting c:\ant\bin in your PATH is advisable, then you could just type ‘ant’) at the command line, gives:

secondant

Look in your bin-debug folder, and you’ll see the SWF file, which should be fully functional.

I think I’ll stop today’s post there.  It has gotten a bit long on me (they always do).  The next post will cover how to get the rest of the files that Flex Builder creates, like the HTML wrapper and Flash-detecting javascript files, incorporated into the build.

Hope this is useful.


Adding Drag/Drop Reorder to ArcGIS Flex Viewer TOC

Wow, that is a long, clumsy title.  Google will like it, though.

Anyway, I’ve been neck-deep in the world of Flex, focusing on the ArcGIS Server Flex API the last couple of months.  We delivered a map viewer based on Flex to a client recently, and it was a great learning experience.  ESRI released a Sample Flex Viewer component that includes the Table of Contents (TOC) sample from the code gallery.  The TOC component was originally built by Tom Hill at ESRI.  He and I have traded a couple of e-mails about adding drag and drop reorder functionality, so I thought I’d run through the basics of what it takes to get it working.

Download the Flex Viewer

If you haven’t already, download the flex viewer from here.  Extract the sample SOURCE (there are two zips, binary and source…we are using the source archive)  to your development area.  I am using FlexBuilder, so my life is easy, as I can just import the existing project into the IDE.  You don’t have to have FlexBuilder, but I am going to presume you know how to edit source files and compile the project.

Modify the LiveMapsWidget

The TOC component is used by the com.esri.solutions.flexviewer.widgets.LiveMapsWidget MXML component.  Open the source for that bad boy (the namespace==the directory).   Find the TOC component, which looks like

<toccomp:TOC id="toc" width="100%" height="100%"/>

We want to enable dragging on the component.  The TOC component extends the core Flex Tree component, so enabled drag drop is as simple as adding

dragEnabled=true

to the element.  If you do that and the run the viewer, you’ll notice you can open the Live Maps widget (in the Globe menu)  and drag the “Lousiana Landbase” layer around.  It doesn’t do much, but you can drag it.  In fact, you get a rex “X” while you drag, which means you can’t drop it anywhere.  So, how do we get rid of that pesky red “X”?   We have to tell the DragManager that the TOC will accept a drag/drop.  The DragManager is a Flex class that maanged drag and drop operations.  Google it for more info (sorry, but I have to constrain the post ot it will go War and Peace on me)   The time to tell the DragManager about the openness of the TOC is when something is dragged (?  drug?) over it.  We do this with the dragEnter event.  So, adddragEnter

dragEnter="onDragEnter(event)"

to the toc element.  Now we have to write the onDragEnter  method.  In the mx:Script area of LiveMapsWidget.mxml, copy this code:

			private function onDragEnter(event:DragEvent):void{
				DragManager.acceptDragDrop(event.currentTarget as TOC);
				if ((event.currentTarget as TOC)==null)
				{
					DragManager.showFeedback(DragManager.NONE);
					event.preventDefault();
				}

			}

This function fires when we drag something over the TOC and tells it that we are open for business. Business is, of course, things that can be dragged and dropped. You could put more logic in here to filter out non TOCItems, etc, but that is left as a lesson for the reader.

OK,  so our red “X” is gone over the TOC (but still there if you drag a layer over the map, cool!)  but now we want it to do something when we drop the layer.  Specifically, we want it to reorder the layers.  But we only have 1 layer, so let’s quickly add another.  Open up the config.xml in the src directory and add the following tag to the <livemaps> element:

<mapservice label="Louisville Public Records" type="dynamic" visible="false" alpha="0.4">http://sampleserver1.arcgisonline.com/ArcGIS/rest/services/Louisville/LOJIC_PublicSafety_Louisville/MapServer</mapservice>

Now we have two layers, making a drag/drop scenario much more compelling.  If you run the viewer you will see both our layers in the LiveMapsWidget.   Now, just like we had to tell Flex to enabled dragging on the TOC, we have to tell it to enabled dropping.  You guessed it, add

dropEnabled="true"

to the toc element.  Now, when you drag/drop a layer, they will reorder in the TOC, which is nice.  The map layers don’t do anything, but we’ll get there.  We have a problem now, though.  The Tree component makes no distinctions between services (roots, in this case) and the layers that make up those services.  This results in the ability of the user to be able to drop a map service under another map service, which is bad.  You can do a lot of checking when the drop occurs to make sure that the user hasn’t dropped one service as a child of another.  I am gonna go an easier route and collapse all the roots (map services) on drag start so those crafty users can’t do it.  They may not like it, but life is sometimes hard.  So, let’s add a dragStart function:

(on the toc)

dragStart = "onDragStart(event)"

(the function)

			private function onDragStart(event:DragEvent):void{
				//Close the dragged item
				_draggedLayer=toc.selectedItem  as TocMapLayerItem;
				var openItems:Array = toc.openItems as Array;
				for each(var o:Object in openItems){
					toc.expandItem(o,false);
				}
				//setInfoText(resourceManager.getString("resource","toc-layer-reorder"));
			}

K…we’re getting there.  Now to the meat of what needs to happen.  When we drop the service, we need to calculate it’s new index in the map, then tell the map to reorder the services.  It took me awhile to find an algorithm that worked for this, and here is why:

  • The basemaps.  The TOC doesn’t have the basemaps, so you have to take that into account when calculating the new index.
  • If the layer is dragged down, then you have to account for that.
  • The TOC indices are reversed form the map services.  So index 0 in the map is the lowest, but it’s the topmost node in the TOC.

So, nothing overwhelming, but details.    Let’s add the event to the TOC element:

dragComplete = "onDragComplete(event)"

and the code:


private function onDragComplete(event:DragEvent):void{
 if (event.action!="move")
 return;
 var _roots:ArrayCollection = toc.dataProvider as ArrayCollection;
 //Unclear why I have to do this....but I need the selectedIndex later
 toc.selectedItem = _draggedLayer;
 var dropIndex:uint=toc.calculateDropIndex(event);
 // I've seen thie calculated drop index be > than the number of 
 // services.  This usually happens when a service node is expanded,
 // but let's just make sure.  We'll put it at the bottom of the list 
 // in this cse.
 if (dropIndex>_roots.length)
 dropIndex=_roots.length;

 var ind:int=0;
 // Set in onDragStart....if it's null, get outta dogdge
 if (_draggedLayer==null)
 return;
 var delta:int = _roots.length-dropIndex;

 ind = delta + 1;//We have two base layers....HACK...THIS IS BAD, MAKE IT BETTER

 // If the selected item is dragged down, then the index needs to account for that
 if (toc.selectedIndex>dropIndex)
 ind=ind-1;

 toc.map.reorderLayer(_draggedLayer.layer.id,ind);

 }

The comments in that function go through what I am trying to do.  I’ll be the first to admit that it isn’t the prettiest code at the ball, but it’s the one I brought so I am dancing with it (Note to self:  work on better analogies)

So, you’d expect it to work now, woudln’t you?  Well, it doesn’t.  We have to make a minor change to the TOC to handle the layer reorder.   In the TOC.as (in src\com\esri\solutions\flexviewer\components\toc) the onLayerReorder function looks like:

private function onLayerReorder( event:MapEvent ):void
{
var layer:Layer = event.layer;
var index:int = event.index;

for (var i:int = 0; i < _tocRoots.length; i++) { var item:Object = _tocRoots[i]; if (item is TocMapLayerItem && TocMapLayerItem(item).layer === layer) { _tocRoots.removeItemAt(i); _tocRoots.addItemAt(item, _tocRoots.length - index - 1); break; } } } [/sourcecode]

Modify the TOC Code (Just a little…)

When you run the viewer now, you’ll get RangeErrors on the addItemAt line above.   So, my approach was to calculate the new TOC index by figuring out the difference between the number of layers and the new index.  Then, make sure somethign didn’t go haywire and we are out of range.  See below:

private function onLayerReorder( event:MapEvent ):void
{
var layer:Layer = event.layer;
var index:int = event.index;
//How far did we move?
var addbackind:int=(map.layerIds.length-1) – event.index;
for (var i:int = 0; i < _tocRoots.length; i++) { var item:Object = _tocRoots[i]; if (item is TocMapLayerItem && TocMapLayerItem(item).layer === layer) { _tocRoots.removeItemAt(i); // If we are out of range on the high end, rein it in if (addbackind>_tocRoots.length)
addbackind=_tocRoots.length-1;
// If we are out of range on the low end, rein it in
if (addbackind<0) addbackind=0; _tocRoots.addItemAt(item,addbackind); break; } } } [/sourcecode] May not be the prettiest...etc, etc.  But it works.  You should now have drag/drop reoder working like a champ.  Obviously, this code could still be improved.  The biggest example is accounting for the basemaps in a cleaner fashion.  I will tell you that we used the Specification Pattern to determine if a service was a basemap, allowing the TOC to ask the specification.  I liked that, but didn't include it here to try and keep this post focused. Anyway, try it out and see how it goes.  If you have improvments or suggestions, hit me in the comments.  Here is a link to the final LiveMapWidgets.mxml I used for this post.