views:

388

answers:

1

I recently completed a project for a custom report UI in Flex. Now, I've been tasked with creating a new application that is essentially a "lite" version of the original UI. It will include only a few of the options that are in the original application. Also, it needs to be a separate application.

I do not want to duplicate my code, so I am planning on moving a bunch of the classes from the original application to a new library that can be shared by both applications. However, I'm trying to figure out how to make this work in my MVC environment.

For example, I have an Accordion component that lets users filter several items. Each Accordion child is an instance of a custom component with two lists (one for entities available for selection, the other for the entities the user has selected). Each child component has properties bound to the Model and functions that call Cairngorm Events.

Here's an simplified example:

FiltersAccordion.mxml:
<?xml version="1.0" encoding="utf-8"?>
<mx:Accordion xmlns:mx="http://www.adobe.com/2006/mxml"
    xmlns:local="*">
    <mx:Script>
        <![CDATA[
            import model.ModelLocator;

            [Bindable]
            private var __model:ModelLocator = ModelLocator.getInstance();
        ]]>
    </mx:Script>

    <local:GenreFilter availableGenres="{__model.availableGenres}" 
         selectedGenres="{__model.selectedGenres}" />

    <local:ArtistFilter availableArtists="{__model.availableArtists}" 
         selectedArtists="{__model.selectedArtists}" />

    <local:LabelFilter availableLabels="{__model.availableLabels}" 
         selectedLabels="{__model.selectedLabels}" />
</mx:Accordion>


GenreFilter.mxml:

<?xml version="1.0" encoding="utf-8"?>
<mx:HBox xmlns:mx="http://www.adobe.com/2006/mxml"&gt;
    <mx:Script>
        <![CDATA[
            import control.events.AddGenresEvent;
            import control.events.RemoveGenresEvent;

            import model.ModelLocator;

            [Bindable]
            private var __model:ModelLocator = ModelLocator.getInstance();

            [Bindable]
            public var availableGenres:ArrayCollection;

            [Bindable]
            public var selectedGenres:ArrayCollection;

            private function addGenresButton_clickHandler():void
            {
                var event:AddGenresEvent = new AddGenresEvent();

                event.availableGenres = availableGenres;

                event.selectedGenres = selectedGenres;

                event.itemsToAdd = availableGenresList.selectedItems;

                event.dispatch();
            }

            protected function removeGenresButton_clickHandler():void
            {
                var event:RemoveGenresEvent = new RemoveGenresEvent();

                event.availableGenres = availableGenres;

                event.selectedGenres = selectedGenres;

                event.itemsToRemove = selectedGenresList.selectedItems;

                event.dispatch();
            }

        ]]>
    </mx:Script>

    <mx:List id="availableGenresList" dataProvider="{availableGenres}" />

    <mx:VBox>
         <mx:Button id="addButton" icon="{rightArrowIcon}" width="22" 
            height="22" click="addGenresButton_clickHandler();" />

         <mx:Button id="removeButton" icon="{leftArrowIcon}" width="22" 
             height="22" click="removeGenresButton_clickHandler();" />
     </mx:VBox>

     <mx:List id="selectedGenresList" dataProvider="{selectedGenres}" 
         width="100%" height="100%" allowMultipleSelection="true" />
</mx:HBox>

ArtistFilter.mxml and LabelFilter.mxml are pretty much the same design as GenreFilter.mxml, but use to their specific Events.

So how should I do this? It doesn't make sense to move my Model into the shared library. I basically want to simply create View components in the library. Am I totally off my rocker here, or what?

+2  A: 

That's one of the big flaws of Cairngorm - your views are tied to your model. That said, you can mitigate a lot of the pain by making the components you want reused more generic and encapsulated. Then, extend them to associate them with the rest of the app.

So, your first component becomes:

<?xml version="1.0" encoding="utf-8"?>
<mx:Accordion xmlns:mx="http://www.adobe.com/2006/mxml"
    xmlns:local="*">
    <mx:Script>
        <![CDATA[
   [Bindable]
   public var availableGenres:ArrayCollection;

   [Bindable]
   public var availableArtists:ArrayCollection;

   [Bindable]
   public var availableLabels:ArrayCollection;

   [Bindable]
   public var selectedGenres:ArrayCollection;

   [Bindable]
   public var selectedArtists:ArrayCollection;

   [Bindable]
   public var selectedLabels:ArrayCollection;
        ]]>
    </mx:Script>

    <local:GenreFilter availableGenres="{availableGenres}" 
         selectedGenres="{selectedGenres}" />

    <local:ArtistFilter availableArtists="{availableArtists}" 
         selectedArtists="{selectedArtists}" />

    <local:LabelFilter availableLabels="{availableLabels}" 
         selectedLabels="{selectedLabels}" />
</mx:Accordion>

Then, you do this in each application (but with different models / events).

<library:SpecialAccordion ... 
 availableGenres="{_model.availableGenres}"
 availableArtists="{_model.availableArtists}"
 ... etc ...
 >

 <mx:Script>
        <![CDATA[
   import model.ModelLocator;

            [Bindable]
            private var __model:ModelLocator = ModelLocator.getInstance();
        ]]>
    </mx:Script>

</library:SpecialAccordion>

Does that make sense? It's the difference between a "view" and a "component." Components can be reused, while views can't. Components are encapsulated, views are application-specific.

Sean Clark Hess
You hit the nail on the head: ModelLocator is just global variables in disguise.
cliff.meyers