Just as I’ve promised an example MVC module was demonstrated and discussed in today’s session. The example is far from perfect but it provides a fundament for discussing various design decisions when we implement an MVC example.
Sample Requirements
The request is to implement a Programmer Statistics Module with the following requirements:
- the module should provide basic and advanced view for a list of programmers
- use text input to filter the list
- use checkbox control to switch between the basic and advanced view
- in advanced view the user can delete programmers
- in advanced view the user can show the programmers statistics
- programmers statistics should be represented by a chart showing the lines of code written in a month by the programmer
- switching back to basic view should make all expanded items to collapse their statistics view
RUN THE MVC EXAMPLE
VIEW MVC EXAMPLE SOURCE CODE
As you look at the working application, you must be able to isolate the Model, the View, the Controller and the Services layers.
The Model
What’s comprising the model?
- collection of programmers’ data
- a flag indicating whether we’re in advanced view
Keep in mind that the list of programmers is pure data – a single item from the list has only data properties and no representation related properties whatsoever.
The View
Among all other controls the View also contains a List component. The abstraction of the list component can be explained basically by the following 3 things:
- data provider – a collection of data items – the List observes all changes happening to this collection and reacts correspondingly by adding or removing list items
- item renderer – a factory for creating item renderer components – the instances created by the factory we call list items
- layout strategy – a strategy for arranging the positions and dimensions of the list items in the list
The List component is the only component that communicates directly with the list items. This is how the List adds a single list item:
private function addItem(itemData : Object) : void
{
var item : IDataRenderer = itemRenderer.newInstance() as IDataRenderer;
item.data = itemData;
listItems.addItem(item);
dataToItemMap[itemData] = item;
UIComponent(item).addEventListener(ResizableItemChangeEvent.DIMENSIONS_CHANGE, handleItemDimensionsChange);
addChild(UIComponent(item));
}
In a way the item’s data property is the item’s model, but since we feed the List with a collection of programmers’ data items, which are pure data this brings the following problem:
How do we initialize all of the properties in the Model of the item renderers’ instances?
One solution to this problem is the application of a pattern called Data Binding Adapter.
The Data Binding Adapter Pattern
The data binding adapter pattern is a way of structuring data binding expressions.
Currently the List is initialized in the following way:
<list:CustomLayoutList id="list" styleName="programmersList" width="550"
layout="{model.inAdvancedView ? verticalLayout : fluidLayout}"
dataProvider="{model.programmers}"
itemRenderer="{new ClassFactory(ProgrammerListItem)}"/>
Let’s try to adapt the collection:
<adapter:ProgrammerListItemAdapter id="adaptedModel"
programmers="{model.programmers}"
inAdvancedView="{model.isAdvancedView}"/>
The adapter is bound to the properties of the original Model and can take actions when the original Model changes – e.g. when the inAdvancedView property changes to false the adapter goes through each item of the itemsModels collection and changes the statisticsShown property to false:
public var itemsModels : ArrayCollection /* of ProgrammerListItemModel */
private function set inAdvancedView(value : Boolean) : void
{
if (!value)
{
for each (var model : ProgrammerListItemModel in itemsModels)
{
model.statisticsShown = false;
}
}
}
And finally we bind the List to the adaptedModel.itemsModels instead of model.programmers:
<list:CustomLayoutList id="list" styleName="programmersList" width="550"
layout="{model.inAdvancedView ? verticalLayout : fluidLayout}"
dataProvider="{adaptedModel.itemsModels}"
itemRenderer="{new ClassFactory(ProgrammerListItem)}"/>
Tell me what you think about this approach?
Containers
Let’s take a look at the StatisticsModule.mxml where all components are wired to each other:
<?xml version="1.0" encoding="utf-8"?>
<mx:Canvas
backgroundColor="0xffffff"
creationComplete="controller.initializeModel();"
xmlns:mx="http://www.adobe.com/2006/mxml" xmlns:model="module.statistics.model.*" xmlns:view="module.statistics.view.*"
xmlns:control="module.statistics.control.*" xmlns:service="module.statistics.service.*">
<mx:Style source="module/statistics/style/statistics.css"/>
<model:StatisticsModel id="model"/>
<view:StatisticsView id="view" width="600" height="100%"
model="{model}"
viewModeChange="controller.switchViewMode();"
deleteButtonClick="controller.deleteListItem(event.target);"
requestStatistics="controller.loadProgrammerStatistics(event.programmer);"
filterChange="controller.filterProgrammers();"/>
<control:StatisticsController id="controller"
model="{model}" view="{view}"
programmersService="{services.programmersService}"
statisticsService="{services.statisticsService}"/>
<service:Services id="services"/>
</mx:Canvas>
Notice anything strange or troubling?The question is:
Do I have to wire the Model, View, Controller and Services inside of a Canvas container? Isn’t it the View, the only View in the MVC? Why is my MVC a container itself?
You can do two different things in this case:
- Strip out the StatisticsView component and include its composite components directly in the module root Canvas. Thus we eliminate one redundant container. A negative side effect of this modification is that the view is not isolated in a self-container component. The following approach is better:
- Use a non-visual component for a root tag. When using a non-visual component for a root tag, there is an extra programming effort to add the View to the application container:
private function addStatisticsModule() : void
{
var module : StatisticsModule = new StatisticsModule();
container.addChild(module.view);
}
Pingback: The VMWare Flex Training Program | Obecto Training Portal
Pingback: Flex Applications’ Architecture | Obecto Training Portal