O'Reilly    
 Published on O'Reilly (http://oreilly.com/)
 See this if you're having trouble printing code examples


Building a Flex Application: Chapter 22 - Programming Flex 3

by Chafic Kazoun and Joey Lott

This excerpt is from Programming Flex 3. If you want to try your hand at developing rich Internet applications with Adobe's Flex 3, and already have experience with frameworks such as .NET or Java, this is the ideal book to get you started. Programming Flex 3 gives you a solid understanding of Flex 3's core concepts, and valuable insight into how, why, and when to use specific Flex features. Learn to get the most from this amazing and sophisticated technology.

buy button

Table of Contents

Introducing the Sample Application
Creating a Flickr Account
Retrieving the Source Code
Setting Up a New Project
Configuring FlickrFlex
Compiling the Application
Running the Application
Utilizing Best Practices
Organizing Files
Using CSS
Application Components
Versioning
Unit Testing
Using Blueprints and Microarchitectures
Abstracting Common Patterns
Understanding Browser Integration
The Business Delegate Pattern
The Model-View-Controller Pattern
Summary

Throughout this book, you’ve learned about all the pieces comprising Flex. But you haven’t seen how these pieces come together to create a working application. An academic knowledge of Flex is essential to successfully building Flex applications, but that knowledge is of little value unless you also have a concrete understanding of how to use what you’ve learned in practice. In this chapter, we’ll look at how to build a Flex application using everything you learned in the first 21 chapters of the book. We’ll walk through building a sample application to illustrate important concepts, and to give you practical experience building a Flex application.

Introducing the Sample Application

In this chapter, we’ll take a look at a sample application that illustrates the key points we discuss in the chapter. The sample application is a simple photo viewer that uses the Flickr API from the popular photo-sharing service, Flickr (http://www.flickr.com), to allow users to search for photos via keywords and then view photo details. We call this application FlickrFlex.

Figure 22.1, “The home page of the FlickrFlex application” shows the application’s home page, which displays a random photo and a search form.

The entire application runs in a web browser, and it supports the browser’s back and forward buttons as well as deep linking directly to a search result or to a specific photo’s details.

Throughout this chapter, we’ll take a look at key parts of the application. What we will not do is go line by line through the code and build the application step by step. Instead, we highlight what we think are important concepts, and then we are leaving it to you, the reader, to download the sample application and review the code in more detail. You may also find that you will gain the most from this chapter if you read it more than once.

Figure 22.1. The home page of the FlickrFlex application

The home page of the FlickrFlex application

Once the user searches for photos, the results are shown in a tile grid (see Figure 22.2, “The results of a photo search, shown in a tile grid”).

Figure 22.2. The results of a photo search, shown in a tile grid

The results of a photo search, shown in a tile grid

The user can click a thumbnail to view the photo details screen, as shown in Figure 22.3, “The photo details screen”.

Figure 22.3. The photo details screen

The photo details screen

Even though FlickrFlex is a relatively simple application, we think it is complex enough that it encompasses many of the common requirements of Flex web applications. For example, FlickrFlex has to solve problems related to making requests to remote services, which is a very common problem for Flex applications. Also, FlickrFlex has to solve the problems related to browser integration, which is quite a common requirement for Flex applications. By looking at FlickrFlex, we think you’ll be able to see ways to solve common problems that you are likely to find in many of your own projects. The purpose of our discussion of FlickrFlex is to look at larger themes and patterns; strategies for solving common application design problems. Therefore, rather than building the entire application one line of code at a time, we’ll highlight the important features and patterns.

Every project starts with the basics. You always have to configure the environment, get access to services, and create projects files. In the next few sections, we’ll look at the steps required to get started.

Creating a Flickr Account

The sample application runs off the Flickr service. To follow along you will need to have a Flickr account and an API key. Both are free.

  • To apply for a Flickr account, go to http://www.flickr.com/signup. Flickr is owned by Yahoo!, and a Flickr account is synonymous with a Yahoo! account. If you already have a Yahoo! account, you don’t need to apply for a Flickr account because you can use your existing account.

  • Once you have logged in with the Flickr account, go to http://www.flickr.com/services/api/keys/apply to apply for a new Flickr API key. The Flickr API requires the API key. To follow along with this sample application, you should apply for a noncommercial key.

Once you’ve created a Flickr API key, you can continue to the next section. You’ll next get the source code for the application, and you’ll need to edit a configuration file with your Flickr API key.

Retrieving the Source Code

Rather than trying to walk you through the steps for writing all the code for the sample application, we’ve opted to simply provide you with complete, working source code. You can then spend less time typing code from listings in the book and more time reading about why the code is written the way it is.

To distribute the source code for the sample application, we’ve created a Google Code project for FlickrFlex. You can view the project site at http://code.google.com/p/flickrflex/. From the Google Code project site, you can download the application source code in one of two ways: using Subversion or as a .zip file.

We recommend using Subversion to download the source code. You can use any Subversion client to check out the project by using the following repository URL: http://flickrflex.googlecode.com/svn/trunk/flickrflex-read-only. This URL will work only with a Subversion client, and not in a web browser.

If you're not comfortable using Subversion (though we highly recommend that you learn how to use it), you can instead download the FlickrFlex application as a .zip file from the project’s download page, at http://code.google.com/p/flickrflex/downloads/list.

Regardless of how you download the project files, we assume that you have downloaded (and extracted, if necessary) all the project files before continuing with the rest of the chapter. Although you can read this chapter and learn much from it without downloading and using the project files, it is likely that you will gain much more if you do follow along with the project files.

Note

Subversion is a version control system. Using a Subversion client and the preceding URL you can download a read-only version of the FlickrFlex project from the Google Code Subversion server. A detailed description of what Subversion is and how to use it is beyond the scope of this book. However, if you want to learn more about Subversion you can read the entirely free Subversion book, Version Control with Subversion, at http://svnbook.red-bean.com. You can also find a list of Subversion client programs at http://subversion.tigris.org/links.html#clients. Using most of these clients is rather simple, especially when checking out a read-only version of a project, and you can generally find instructions for how to check out a project in the documentation for each client.

Setting Up a New Project

If you are using Flex Builder 3, you can simply import the Flex project, as we’ve included all the Flex Builder project files along with the source code. If you are not using Flex Builder, you will need to compile the application using mxmlc, as you learned how to do in Chapter 2, Building Applications with the Flex Framework. Flickr.mxml is the main application .mxml file for FlickrFlex, and TestRunner.mxml is the main .mxml file for running the unit tests for the project.

Configuring FlickrFlex

The code for the FlickrFlex application is complete and working exactly as downloaded. However, you will need to provide your Flickr API key and secret, both of which you can find at http://www.flickr.com/services/api/keys/. You should locate and edit the assets_embed/xml/flickrconfig.xml file in the application project files. The file contains the following:

<configuration>
    <apiKey>Your API key</apiKey>
    <secret>Your API secret</secret>
</configuration>

You should replace your API key and your API secret text with the correct values from the Flickr page.

Compiling the Application

Now that you’ve downloaded and configured the FlickrFlex project, the next step is to compile it. This is a straightforward step since there is nothing particularly unusual about the project. If you are using Flex Builder and you’ve imported the Flex project, you’ll probably note that two of the .mxml files are set to be compilable: TestRunner.mxml and Flick.mxml. The Flickr.mxml file is the file you want to compile, and in Flex Builder the compilation step is automatic unless you’ve disabled automatic builds for the project. That means all you need to do is run the Flickr.mxml file to compile and launch it.

If you’re using the Flex SDK without Flex Builder, you will need to compile Flickr.mxml using mxmlc. FlickrFlex does not require any additional libraries or any unusual compiler options.

Running the Application

When you have successfully followed the preceding steps, you should be able to compile and run the application. You’ll know the application is working correctly when you can verify the following user stories:

  • The user sees a random “nature” photo on the home page.

  • The user can enter a search term on the home page and request search results.

  • When search results are returned, the user sees thumbnails and titles for all search results displayed in a scrollable grid.

  • When the user clicks on a thumbnail in the search results, she is taken to that photo’s details screen in which she sees a large version of the photo along with the title and description (if any exist).

  • From the details screen the user can click a button to return to the previous screen.

  • From the details screen the user can click Next and Previous buttons to navigate through details of the search results.

  • The user can navigate back and forward between viewed pages/screens by using the back and forward web browser buttons.

  • The user can copy the address from the browser address bar at any point and paste it into another browser window to see the same page/screen (a process known as deep linking).

Utilizing Best Practices

There are lots of ways build Flex applications. This shouldn’t surprise you; there are lots of ways to do most things in life, and building Flex applications is no different. Furthermore, it’s impossible to say there is exactly one right way to build Flex applications, just as it is impossible to say there is just one right way to do anything else. However, you can employ certain strategies that increase your likelihood of success when building Flex applications. We call these best practices, and in the following sections we’ll go into a bit more detail regarding a few of the best practices we consider most important. We strongly encourage you to try these out for yourself. Give them a fair chance; you may later discard some of them, but chances are that more than a few will prove to help you and your teams in the long run.

Organizing Files

How you organize project files can have a tremendous impact of the success of a project. File organization might seem unimportant, but don’t underrate how influential this factor can be. Organizing files well ensures that not only is it easier to find the files and easier for teams to work together, but also you avoid potential naming conflicts and can facilitate source code versioning (see the section called “Versioning”” later in this chapter).

If you use Flex Builder to create Flex projects, you have probably already noticed that Flex Builder provides a degree of organization. Primarily, Flex Builder distinguishes between where it stores source code (.mxml and .as files) and where it stores deployable application files (.swf, .html, and runtime assets). The default names for the directories that Flex Builder uses are src for source code and bin-debug for deployable application files. This high-level structure is important and essential to good project file organization. However, you can do more, and indeed more is required to keep files organized clearly. The following are a few of the key things you can do to organize files:

  • Place all .mxml and .as files (except for the main application .mxml file) in packages. As you’ve learned, Flex treats .mxml files as ActionScript classes, and therefore we can think of them and organize them just like we would ActionScript classes. We generally recommend using reverse-domain names for packages. For example, all the classes and .mxml files for the sample application in this chapter are organized within a com.oreilly package. After the reverse-domain package, we advise using a project subpackage where appropriate. In the sample application, we place all the files in a pf3 project subpackage. Then it is considered a best practice to further organize files into subpackages. For the sample application, we use the following subpackages:

    controllers

    See the section called “Abstracting Common Patterns”” later in this chapter for more information about controllers.

    events

    We use this package for all event types (subclasses of Event).

    models

    See the section called “Abstracting Common Patterns”” for more information about models.

    services

    This package organizes all the business delegate and related classes.

    views

    See the section called “Abstracting Common Patterns”” for more information about views.

  • Place all embedded assets in a directory inside the source files directory. In the sample application, we call this directory assets_embed. This directory can contain .css files, embedded .swf files, embedded image files, and generally any sort of file that might be embedded in the application (as opposed to loaded at runtime). Depending on the number of files, it may make sense to create further subdirectories to better organize everything within the embedded assets directory.

  • Any runtime assets that must be deployed with the application should be organized within the deployable application directory (bin-debug). These files might include .xml files, image files, or any sort of file that the application loads at runtime. Generally, these files should be organized within subdirectories within the deployable application directory.

Using CSS

Flex provides a lot of ways to apply styles to applications. You learned about all of these ways to style applications in Chapter 8, Customizing Application Appearance. This provides a great deal of flexibility in how to style an application, which is both good and bad. It is good in that it provides lots of options. It is bad in that it allows developers and teams of developers to apply styles without a consistent approach or plan.

We find that in general, it is best to apply most, if not all, styles via external .css files. We make exceptions for some layout-related styles such as verticalAlign and horizontalAlign, which we frequently apply inline. However, we prefer to apply the vast majority of styles via external .css files. For the Flickr application, we use only one .css file because the application simply isn’t complex enough to warrant more than one .css file.

If you look at the FlickrFlex.css file, you’ll notice that we’ve organized our selectors to make them somewhat easy to locate within the file. We place the Application type selector first in the file, followed by the rest of the type selectors in alphabetical order. Then we follow the type selectors with the class selectors, also organized in alphabetical order.

Although it is sometimes tempting to apply styles via inline attributes to MXML tags, we’ve found it is far better in the long run to take the extra minute or two to apply styles via external .css. This has several advantages, some of which are as follows:

  • External .css cleans up style-related clutter from .mxml files.

  • External .css allows non-Flex developers to work on styling an application.

  • External .css helps to differentiate between the layout (view/.mxml) and style (.css).

Application Components

Application components are invaluable and cannot be overrated. Large .mxml files or ActionScript classes without clearly defined roles and responsibilities are a good sign that refactoring is necessary. When an application consists of many smaller components, it is often easier to work as a team, to debug an application, and to add new features or make changes to existing features. Too few components, or components that are too large and too complex, make applications difficult to manage. It is generally advisable to err on the side of too many components with responsibilities that are too granular than the other way around. As a rule of thumb, all components should have no more than two or possibly three key responsibilities. If a component has more responsibilities, it should be refactored into several more specialized components.

As we’ll see later in this chapter, we use application components extensively for the parts of the application that we call the views and the controllers. Almost universally, developers use application components for views. Our use of application components for controllers is somewhat less usual, but we think it provides a solution to issues that are unique to Flex. You can read more about views and controllers later in this chapter. You can read more about application components in general in Chapter 9, Application Components.

Versioning

Although versioning may seem like a related practices rather than a core practice, it is nevertheless a core practice that every team should be following for Flex projects. A variety of versioning software tools are available. Some of the most popular are Subversion (which we are using for the sample application), CVS, Mercurial, Git, Team Foundation Server, and Visual SourceSafe (VSS). Every versioning software works slightly differently, and different teams have different philosophies regarding how to manage code repositories. For example, some teams prefer not to add large assets (e.g., videos) to version control repositories, whereas other teams prefer the simplicity of adding everything to one location. What is most important is that you always add all source code to a repository, and you should always keep the repository up-to-date with changes. For the FlickrFlex project and many of our own projects, we like to add everything to the Subversion repository, including source code, Flex Builder project files, runtime assets, and compile time assets (embedded images, fonts, etc.).

Unit Testing

Unit testing involves writing code that runs automated tests on project code. A test will run an assertion that will either pass or fail. A group of tests is known as a suite of tests. When running a suite of tests, you can quickly determine whether code is behaving as expected. If all the tests in a suite pass, it indicates that there is a good chance the application is working correctly. If one or more tests fail, it shows you where you should look to correct a problem with the code. Automated tests allow you to run suites of tests frequently, often every time you make a change to the code for an application. The results of the test will help you immediately identify any problems which new code might introduce.

Unit testing does not require that you use a testing framework. However, using an existing testing framework can greatly simplify writing and running test suites. There are two testing frameworks that we know of for use with Flex: FlexUnit (http://code.google.com/p/as3flexunitlib/) and AsUnit (http://www.asunit.org). We find that FlexUnit is a little easier to use with Flex than AsUnit.

Note

If you’re not familiar with unit testing or FlexUnit, you might find the article at http://www.adobe.com/devnet/flex/articles/unit_testing.html to be a helpful introduction.

Once you’ve starting writing unit tests, you’re likely to ask yourself exactly what you should be testing. Although it might be possible to write tests for every part of a Flex application, we find that it is generally easiest and most effective to write tests for only certain parts of an application. In particular, we find that tests for the following are most useful:

Data model classes that have custom serialization or deserialization methods

For example, if a class has a method to deserialize from XML data, it can be very useful to write a test or tests for that.

Business delegates

These are points of integration that represent relatively high fragility. If the contract with the service changes (e.g., the publisher of the service changes the API), it’s possible that the entire Flex application will stop working correctly. Having good unit tests for business delegates helps you to quickly identify where an error is occurring if a service contract changes or if a service simply goes offline or throws an error.

Utility classes/methods

Any method of a utility class should generally have unit tests.

There is a philosophical approach to software development, known as test-driven development (TDD). This methodology advocates writing tests before writing application code. You then write the application code to get it to pass the tests. Some Flex developers find TDD works well for certain types of classes, such as model classes and business delegates. Other Flex developers don’t find that TDD is well suited to Flex development. We are mixed on the issue of TDD for Flex development, with one of us liking it and the other not. You’ll have to be the judge for yourself.

Note

You can learn much more about test-driven development in the excellent book, Test Driven Development by Example, by Kent Beck (Addison-Wesley).

The sample application has a suite of tests that are included in the com.oreilly.pf3.flickr.test package, and the application also includes a test runner, TestRunner.mxml, to run the tests. You can see that there are two classes in the test package: PhotoTest and FlickrServiceTest. The PhotoTest case runs tests verifying that a Photo object correctly deserializes XML data as it is returned by the Flickr service. From a TDD perspective, it is useful to write this test before writing the Photo class and its deserialization method. Having the test written first can help to verify that Photo does what it is intended to do and that it meets all the requirements placed on it as you write it. However, even after the class has been written, the tests are useful because you can run them to verify that no regressions have occurred as you refactor the application. Likewise, it can be helpful to write the FlickrServiceTest class before writing FlickrService, and yet it is also useful after writing FlickrService to verify that the service works as intended.

Using Blueprints and Microarchitectures

Even with the best practices mentioned in the preceding section, the idea of building a Flex application from scratch can be a bit daunting. Where should you begin? Sometimes too much freedom can be paralyzing. Artists often report that their greatest creativity stems from having boundaries. Likewise, you may find that having structure may provide the right environment for building a successful Flex application. This is the role of a microarchitecture.

Microarchitectures are intended to provide a general guideline and structure for building applications. A microarchitecture doesn’t deal with the specific requirements of a specific application, but rather concerns itself with a philosophical approach to how applications should be structured and designed in general. Microarchitectures provide these oft-needed boundaries and guidelines that help remove the paralysis you might otherwise experience. To that end they can be useful.

Various individuals and organizations promote many microarchitectures for Flex applications. A few of the more popular of these are as follows:

We don’t advocate the use of any one microarchitecture over another. In fact, we don’t really advocate the use of microarchitectures at all. Instead, we recommend that you find a system that works best for you. For the sample application, we are borrowing some of what we consider to be the best ideas from our own experiences building applications with Flex.

Although any one of these microarchitectures might be helpful and useful, don’t be surprised if it is limiting at the same time. The boundaries created by a microarchitecture can provide structure, but they are still boundaries. If you decide to use one of these microarchitectures, it is probably a good idea to follow the rules and build applications exactly as specified by the microarchitecture until you are familiar with the basics and have built several applications successfully. However, at that time you may find it useful to break the rules a little and adapt them to what works best for you. To really take our advice on this matter you’ll probably need to be fairly proficient with Flex and with basic design and architectural patterns before you even start working with a microarchitecture. That’s not because we think microarchitectures are “advanced” or “difficult.” Rather, it’s because we think that to be able to have a critical approach to using a microarchitecture (such that you can learn from the patterns without having to adopt the entire system blindly) you need to have a fair amount of Flex and application development experience first. For the sample application in this chapter, you’ll find that we’re not stressing that you must build your own applications using the same design structure we’ve used. Instead, we’re simply providing one example that uses patterns that we’ve found to be useful for Flex applications.

Abstracting Common Patterns

Like all software, Flex applications are complex. This requires that developers building Flex applications are able to solve architectural and design problems rather than simply throwing together a bunch of off-the-shelf components. Using a microarchitecture as described in the preceding section can help in this regard. However, when using a microarchitecture you can solve only the problems that the microarchitecture is specifically designed to address. If a problem falls outside that scope, the problem remains unsolved. In this manner, it’s perhaps more useful to at least understand some of the common patterns that are used to solve problems. You can then utilize these patterns either as part of or apart from a microarchitecture.

Many papers, articles, and books have been written about design and architectural patterns used in software development. The patterns that you could use when building a Flex application are too numerous to list and detail in this chapter. However, a few patterns are common enough that we would be remiss not to describe them. Notably, nearly every Flex application could profit from the use of the Model-View-Controller pattern and the Business Delegate pattern, both of which we’ll discuss in more detail in the following sections. Additionally, we’ll start by looking at a pattern for dealing with browser integration, which is a problem that is unique to web-based applications.

Understanding Browser Integration

Shortly we’ll look at much of the code in more detail. However, before we do so, you’ll need to understand how the FlickrFlex application handles browser integration (deep linking and the back and forward buttons). The principle is rather simple: all significant state change requests are routed through the BrowserManager class by updating the address fragment. For example, when the user clicks on a Search button, the FlickrFlex application does not directly change the state to the search results screen or immediately make the request to the search method on the Flickr service. Instead, the application updates the address fragment via BrowserManager and waits for BrowserManager to dispatch an event notifying the application to update as necessary. We’ll see a few specific examples of this later on. However, the basic way in which the application manages this behavior depends on a simple infrastructure provided by a few methods in a few of the .mxml files, which we’ll look at now.

The main application .mxml file is Flickr.mxml. This file contains little more than a Style tag and an instance of FlickrController. The FlickrController class is where we start to set up browser integration. Within the initializeHandler() method, we tell the application to listen for changes to the URL via the BrowserManager:

_browserManager.addEventListener(BrowserChangeEvent.APPLICATION_URL_CHANGE,
                                 urlChangeHandler);
_browserManager.addEventListener(BrowserChangeEvent.BROWSER_URL_CHANGE,
                                 urlChangeHandler);

When the URL changes, the first thing we do is parse the fragment into an array, using a forward slash as a delimiter:

var fragments:Array = _browserManager.fragment.split("/");

We then remove the first element from the array and use that to determine what state to set on the view. If, for example, the fragment is search/nature (which is what the fragment would be if the user runs a search using the term nature), the first element in the array will be search, which means we want to tell the view to change to the search screen. In every case, we pass along the remaining fragment to the view:

var topFragment:String = fragments.shift();
if(topFragment == "search") {
    view.setMode(FlickrView.MODE_SEARCH_SCREEN, fragments.join("/"));
}
else if(topFragment == "") {
    view.setMode(FlickrView.MODE_HOME, fragments.join("/"));
}
else if(topFragment == "details") {
    view.setMode(FlickrView.MODE_PHOTO_DETAILS, fragments.join("/"));
}

Within FlickrView’s setMode() method, we change the state and then assign the remaining fragment to the screenFragment property of the corresponding screen if appropriate:

public function setMode(mode:String, fragment:String = null):void {
    currentState = mode;
    if(fragment != null && fragment.length > 0) {
        if(mode == MODE_SEARCH_SCREEN) {
            searchScreen.screenFragment = fragment;
        }
        else if(mode == MODE_PHOTO_DETAILS) {
            photoDetailsScreen.screenFragment = fragment;
        }
    }
}

If the entire fragment was search/nature, the setMode() method would change the state to the search screen state and assign nature to the screenFragment property of searchScreen. Later in the chapter, when we talk about views and controllers, we’ll see how the search screen handles the fragment. However, at this point the specifics of how fragments are ultimately utilized in specific views and controllers aren’t as important as the overall pattern. To summarize and restate: for FlickrFlex we have a hierarchical structure of objects (FlickrController contains FlickrView, FlickrView contains HomeController, etc.). FlickrController listens for URL changes, and when the fragment changes, FlickrController starts to trickle down the fragment updates by notifying FlickrView. FlickrView uses the fragment to decide how to change state, and it then passes along the remainder of the fragment to the controller corresponding to the new state. This pattern could theoretically be continued for as many levels of hierarchy that exist.

The Business Delegate Pattern

The majority of Flex applications utilize a service of one sort or another, whether the services are SOAP services, REST services, AMF (Remoting) services, or any other sort. Elements of the Flex application need to interact with these services. For example, a user may request from a service a list of states or provinces for a given country, or a user may fill out a form in Flex and then submit the form to a service method.

In a naïve approach, the actual implementation that a Flex application uses to interact with a service may be exposed directly to all elements within the Flex application. For example, if a part of the application needs to query a SOAP service for a list of states or provinces, it is possible to allow that part of the application to create an instance of the WebService component with the appropriate operation, and to bind the results of the operation directly to a UI component. This may work, but it is a shortsighted solution that creates fragility.

Instead of the aforementioned solution, you can use a common pattern known as the Business Delegate pattern. A business delegate is a class that serves as a proxy to a remote service, defining an API that the rest of the application can use without having to know anything about the implementation details. That way, the rest of the application only has to know about its contract with the business delegate. If the implementation details change, the rest of the application doesn’t have to change as long as the business delegate API remains the same. For example, an application may initially utilize a set of SOAP web services. Later the services may be migrated over to use Remoting (AMF) instead. If the application doesn’t use a business delegate, many parts of the application may need updating to work with the new services. However, if the application uses a business delegate, only the business delegate needs to change.

FlickrFlex uses the Business Delegate pattern. In this application, we define a class called com.oreilly.pf3.flickr.services.FlickrService. This class allows us to define a simple API for the rest of the FlickrFlex application to use without having to know anything about the implementation details. The FlickrService class defines only three methods: searchPhotosByText(), getPhotoDetails(), and getPhotoSizes(). The rest of the application can call these methods without having to know whether FlickrFlex is actually making requests to local XML files or a remote service. However, it turns out that in this example we are going to have the application connect to a remote service.

In the FlickrFlex application, we are connecting to only one service: the Flickr service. We don’t want to get too bogged down in the details of the Flickr service since our primary focus in this chapter is to better understand common Flex application design and development problems and solutions. However, to make sense of the code for the sample application we’ll need to explain just a few things about working with the Flickr service.

For the purposes of the sample application, we’re necessarily using only a small subset of the Flickr API. We’re calling only three Flickr service methods: one to search photos, one to get information about a specific photo, and one to get the different sizes of a specific photo.

Note

You can always read more about the Flickr API, including all the available methods and their signatures, at http://www.flickr.com/services/api.

The Flickr service uses what is known as representational state transfer, more commonly known as REST. REST uses URLs to access resources. The Flickr service allows us to call methods via resources. In the case of the sample application, there are three methods that we relate to business delegate methods: flickr.photos.search, flickr.photos.getInfo, and flickr.photos.getSizes. Next we need to look at how to access these methods.

You must use the same pattern to access any Flickr resources. You must make an HTTP request to the REST resource along with a query string indicating the method and parameters. The REST resource is http://api.flickr.com/services/rest/. Therefore, you can see that in FlickrService we define a constant called REST_URL as follows:

static private const REST_URL:String = "http://api.flickr.com/services/rest/?";

Note

The trailing ? is there to precede the requisite query string that we will look at next.

The query string for Flickr service requests must always include the method name along with the required parameters for that method. The Flickr API documentation lists the required parameters for each method. All methods require the API key. You can see that in FlickrService we define three methods that call REST service methods, each method calling a different service method with different parameters, but each following the same basic pattern. We’ll look at searchPhotosByText(). This method calls the flickr.photos.search method, which requires a text parameter, which is a comma-delimited list of words for which to search.

public function searchPhotosByText(text:String):PendingOperation {
    text = text.split(" ").join(",");
    var method:String = "flickr.photos.search";
    var parameters:Array = new Array();
    parameters.push(new NameValue("api_key", _apiKey));
    parameters.push(new NameValue("method", method));
    parameters.push(new NameValue("text", text));
    var url:String = REST_URL + createQueryString(parameters);
    var pendingOperation:PendingOperation = new PendingOperation(url);
    return pendingOperation;
}

You can see that this method creates an array of NameValue objects, which are simply objects with name and value properties, and then passes that array to createQueryString() to create the query string, which it appends to the REST URL.

If we look next at createQueryString(), we can see that it simply takes all the names and values and strings them together with = and & delimiters:

private function createQueryString(parameters:Array):String {
    var queryString:String = "";
    var i:int;
    for(i = 0; i < parameters.length; i++) {
        queryString += parameters[i].name + "=" + parameters[i].value + "&";
    }
    return queryString;
}

The searchPhotosByText() method then returns a new PendingOperation object, passing it the newly constructed URL. The PendingOperation class simply makes an HTTP request to the specified URL, and it proxies the response, dispatching an event when the response is returned.

package com.oreilly.pf3.flickr.services {

    import flash.events.Event;
    import flash.events.EventDispatcher;
    import flash.events.HTTPStatusEvent;
    import flash.net.URLLoader;
    import flash.net.URLRequest;

    import mx.rpc.events.FaultEvent;
    import mx.rpc.events.ResultEvent;

    public class PendingOperation extends EventDispatcher {

        static public const RESULT:String = "result";
        static public const ERROR:String = "error";

        private var _loader:URLLoader;
        private var _vo:*;

        public function PendingOperation(url:String, vo:* = null) {
            _loader = new URLLoader();
            _loader.addEventListener("complete", resultHandler);
            _loader.addEventListener(HTTPStatusEvent.HTTP_STATUS, errorHandler);
            _loader.load(new URLRequest(url));
            _vo = vo;
        }

        private function resultHandler(event:Event):void {
            dispatchEvent(new ResultEvent(RESULT, false, true,
ParsingUtility.parse(new XML(_loader.data), _vo)));
        }

        private function errorHandler(event:HTTPStatusEvent):void {
            dispatchEvent(new FaultEvent(ERROR));
        }
    }
}

You can see that the PendingOperation class relies on a method of the ParsingUtility class. We’re using this custom ParsingUtility class to determine what type of response the service has returned and convert it to the correct type. For example, when the PendingOperation object is handling the response from a flickr.photos.search method, the ParsingUtility class detects the response as such and converts the XML into an ArrayCollection of Photo objects, which is exactly what the rest of the FlickrFlex application expects.

We’ve designed the FlickrService and PendingOperation classes in this way so that the rest of the application needs to know nothing about the implementation details. As far as the controllers and views and the rest of the application are concerned, it makes no difference whether the Flickr service is a REST service. If Flickr decided to change to an AMF service at some point, we could adapt the application with little to no changes to any of the code outside the services package.

The Model-View-Controller Pattern

The Model-View-Controller (MVC) architectural pattern is utilized across many languages and platforms. If you have experience building software of any sort, chances are MVC is familiar to you. Volumes have been written about this pattern, and we won’t try to repeat much of what has already been said. Instead, we’re primarily concerned with the usefulness of the pattern in Flex applications and the nuances of how to implement it practically in Flex. However, if you aren’t already familiar with this pattern, we’ll outline it briefly.

The principle concern of MVC is to effectively separate the business logic and the user interface code such that they interact with one another only through well-defined programming interfaces. The idea is that this approach reduces fragility because you can make changes to one without having to necessarily make changes to the other.

As the name implies, this pattern typically uses three parts—model, view, and controller:

  • The model is the way in which data is represented programmatically.

  • The view is the user interface. Typically, a view does not directly modify the model it represents. Instead, it allows the controller to modify the model, and the view updates itself when the model changes. Additionally, usually a view does not call methods of its controller, nor does it even have a reference to a controller. Instead, a view usually merely dispatches events that a controller can handle.

  • The controller is the business logic. A controller typically has a reference to a model and a view, and it uses these references to modify the model and call methods of the view directly.

Although it’s possible to write models, views, and controllers in a 1:1:1 ratio, it’s also possible that a controller could be used with many different views and that a model could likewise be used by many different views and controllers.

You can implement the MVC pattern in many different ways at many different levels. At one level, it is possible to create one model and one controller for an entire application (which uses many different views). It is also possible to implement the pattern at a more granular level, creating controllers, models, and views for all components. How you implement the pattern will depend on the complexity of the application and your preferences as a developer. In the FlickrFlex application, we have opted for a more granular approach.

Next we’ll look at creating models, views, and controllers in Flex applications.

Models

Although there are variations on how to implement a model in a Flex application, those variations are relatively narrow in scope, and for the most part Flex developers tend to build models in a very similar fashion. At the core of the model implementation is something called a business object (or sometimes more specifically a value object). A business object is a type that represents entities (logical groupings of data) within the business domain (application). A value object is a more specific type of business object that has no additional logic applied to it, and merely stores data. In the case of FlickrFlex, we have only one business object, a class called com.oreilly.pf3.flickr.model.data.Photo. The Photo class is a fairly standard implementation. It models the same basic data as is returned by the various photo-related service methods, defining private properties for all of them:

private var _id:String;
private var _owner:String;
private var _secret:String;
private var _server:String;
private var _farm:String;
private var _title:String;
private var _thumbnailUrl:String;
private var _description:String;
private var _tags:ArrayCollection;
private var _imageMediumUrl:String;
private var _imageLargeUrl:String;

The class then defines accessor methods for most of the properties, as in the following:

public function get id():String {
    return _id;
}

Also, a few properties can be updated after the initial creation of the object. Therefore, we create mutator methods as well, and because we want user interface elements (views) to be able to update themselves based on the changes, we set the properties as bindable. The description property is an example:

[Bindable(event="descriptionChanged")]
public function set description(value:String):void {
    _description = value;
    dispatchEvent(new Event("descriptionChanged"));
}

public function get description():String {
    return _description;
}

Various developers have different perspectives on how a business object should be created. Some developers are of the opinion that business objects should not know anything about the manner in which they are created. Other developers prefer to make business objects responsible for creating themselves, given specific input. For Photo, we’ve opted for the latter approach, creating a static parseFromXml() method that accepts the return XML from a Flickr photo search, and constructs and returns a corresponding Photo object:

static public function parseFromXml(xml:XML):Photo {
    var id:String = xml.@id;
    var owner:String = xml.@owner;
    var secret:String = xml.@secret;
    var server:String = xml.@server;
    var farm:String = xml.@farm;
    var title:String = xml.@title;
    return new Photo(id, owner, secret, server, farm, title);
}

Frequently, applications require complex models, and it can be useful to maintain an application-wide reference to the models used by various parts of the application. For this purpose, a model locator can be a valuable part of the model. The model locator is typically implemented using the Singleton design pattern, and it stores references to various models in use. For the FlickrFlex application, we’ve defined a model locator called com.oreilly.pf3.flickr.model.ApplicationModel.

The model locator for FlickrFlex is an application-wide repository for stateful model data, including the following:

  • The current search term

  • The current search results

  • The selected photo

  • The index of the selected photo in the search results

Controllers and views

The implementation of controllers within Flex applications differs among developers much more widely than does the implementation of models. As we indicated earlier, it is possible to build an application that uses one application-wide controller. This is an approach advocated by some microarchitectures such as MVCS. This is certainly a workable approach, and it has its advantages. However, one drawback of this approach is that the one controller is generalized, and the larger and more complex the application becomes the larger and more complex the controller becomes. Therefore, for many applications it is advantageous to use more granular controllers that are specific to components of the application. This is the approach we use with the FlickrFlex application.

Implementing controllers in Flex can be challenging because of the way Flex is designed. Typically, views are user interface elements and controllers are not. As a result, it may initially seem to make sense to have components create and maintain a reference to their own controller. However, this approach has serious drawbacks; most notably it reverses the typical relationship between views and controllers. A controller should provide the programmatic interface to a component, and it should be able to theoretically control any view that implements the interface the controller expects. By having a view construct a controller, these roles and behaviors are no longer possible.

With the FlickrFlex application we’ve taken a slightly unconventional approach. We’ve decided to define controllers using MXML, making controllers extend UIComponent. This enables controllers to be added as children of other containers, and it allows controllers to then add views to themselves. If you look in Flickr.mxml, you’ll notice that it is very simple. In this document, we create an instance of FlickrController:

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="vertical"
xmlns:views="com.oreilly.pf3.flickr.views.*" xmlns:controllers="com.oreilly.
pf3.flickr.
controllers.*" backgroundColor="#4F5050">
    <mx:Style source="assets_embed/css/FlickrFlex.css"/>
    <controllers:FlickrController/>
</mx:Application>

In FlickrController we create only one component instance: an instance of FlickrView. FlickrController is the control for FlickrView.

Inside FlickrView you can see that in the various states we create instances of different controllers: HomeController, SearchController, and PhotoDetailsController. In each controller document we create just one component instance: HomeScreen, SearchScreen, and PhotoDetailsScreen, respectively. If you look at the code for these views, you’ll notice that they don’t reference the controllers. Instead, if any communication is necessary with the controllers, it is achieved by dispatching events.

Next we’ll look specifically at the SearchScreen and SearchController to better understand how these two classes are designed and how they work both together and in the context of the application.

In the com.oreilly.pf3.flickr.views.screens package you’ll see SearchScreen.mxml, the view for the search form/search results in the FlickrFlex application. SearchScreen is fairly representative of the rest of the views in the application and how we typically build views in general. The key points to notice about SearchScreen are as follows:

  • The view defines an API to grant access to anything related to its own state, including changes to components within it. You’ll notice that SearchScreen defines getters and/or setters for the following: dataProvider, searchTerm, isSearching, and selectedPhoto. We’ll talk more about each in just a moment.

  • The view does not have any reference to a controller. You can see that SearchScreen doesn’t know anything about SearchController. However, there are a few instances where SearchScreen needs to communicate with its controller. This is achieved through dispatching events. You can see that when the user clicks on an item in the tile list the view dispatches an event, and it dispatches a different event when the user clicks on the Search button.

The SearchScreen component, as with all views, is primarily responsible for user interface layout and for relaying user interaction to a controller via events. The SearchScreen consists of a search form (a text input and a button) and a tile list, as shown in the following snippet from the code:

<mx:HBox width="100%" height="45" backgroundColor="#212122" verticalAlign="middle">
    <mx:HBox styleName="green" height="100%">
        <mx:Label text="tags" />
        <mx:TextInput id="searchTermInput" text="{_searchTerm}" />
        <mx:Spacer width="10" />
        <mx:Button id="searchButton" label="Search for Photos"
            styleName="green" height="100%"
            click="dispatchEvent(new PhotoSearchEvent(searchTermInput.text));" />
    </mx:HBox>
</mx:HBox>
<mx:TileList id="photoList" width="100%" height="100%"
    itemRenderer="com.oreilly.pf3.flickr.views.renderers.PhotoRenderer"
    dataProvider="{_dataProvider}"
    change="dispatchEvent(new PhotoSelectEvent(photoList.selectedItem.id));" />

You can see that in the two cases where there is user interactivity (the user clicking on the Search button or selecting an item from the tile list), the view merely dispatches an event.

As mentioned earlier, the view defines an API for reading and writing values and/or state on the view. One of the key getters/setters is dataProvider, which is fairly standard for most views. The dataProvider getter/setter merely gets/sets the bindable _dataProvider property, which is an ArrayCollection in the case of SearchScreen. The tile list is bound to the _dataProvider property such that whenever the property changes, the tile list reflects those changes. We’ll look at when and how this property changes when we look at the controller code in just a minute.

The other two getters/setters are searchTerm and isSearching. The searchTerm getter/setter is used to get and set the keyword(s) displayed in the search form, when applicable. The isSearching getter/setter is used to change the state to disable the view when a search is running or when the result is returned from the service.

The selectedPhoto getter is designed to return a reference to the Photo object corresponding to the selected tile list item. This provides a well-defined API such that the controller can access that information without having to know anything about how the view is implemented.

The SearchController class is designed to handle all the business logic for searching, displaying results, and navigating to photo details. The SearchController has an instance of SearchScreen with an ID of view:

<screens:SearchScreen id="view" photoSearch="photoSearchHandler(event);"
photoSelect="photoSelectHandler(event);" />

When the photoSearch or photoSelect event occurs (dispatched by the view), the controller handles the event. In both cases, the controller handles the event by updating the browser address fragment using BrowserManager:

private function photoSearchHandler(event:PhotoSearchEvent):void {
    BrowserManager.getInstance().setFragment("search/" + event.searchTerm);
}

private function photoSelectHandler(event:PhotoSelectEvent):void {
    BrowserManager.getInstance().setFragment("details/" + event.photoId);
}

You’ll recall from earlier in the chapter that state changes are often managed by changes to the browser address (via BrowserManager). You may recall from that discussion that when changes are detected in the URL, FlickrController tells FlickrView to change its state, and FlickrView may then pass along part of the address fragment to the corresponding controller via a screenFragment property. Therefore, when the fragment is changed to search/search term, the search term gets assigned to the screenFragment property of SearchController. Therefore, we define a screenFragment setter as follows:

public function set screenFragment(value:String):void {
    value = unescape(value);
    ApplicationModel.getInstance().searchTerm = value;
    if(_searchTerm != value) {
        view.searchTerm  = value;
        searchForPhotos(value);
    }
}

You can see that if the search term is the same as the existing search term, no further action is necessary. However, if the search term is different, the controller updates the search term for the view and runs a method (searchForPhotos()) that uses the FlickrService instance to query the Flickr service for photos with that search term:

private function searchForPhotos(searchTerm:String):void {
    _searchTerm = searchTerm;
    view.isSearching = true;
    var pendingOperation:PendingOperation = _service.searchPhotosByText(_searchTerm);
    pendingOperation.addEventListener(PendingOperation.RESULT,
                                      searchForPhotosResultHandler);
}

You can see that the controller sets isSearching to true. When the result is returned, not only does the handler method update the dataProvider of the view, but it also sets isSearching to false:

private function searchForPhotosResultHandler(event:ResultEvent):void {
    var photos:ArrayCollection = event.result as ArrayCollection;
    ApplicationModel.getInstance().searchResults = photos;
    view.dataProvider = photos;
    view.isSearching = false;
}

Summary

Throughout this book, you learned a great many things about Flex 3. In this chapter, we tied much of that together with a sample application and looked at a set of simple best practices. In the course of this chapter, you had the opportunity to download a complete Flex 3 sample application that uses the Flickr service to search photos and display results. We then discussed some of the key architectural decisions in the application.

This excerpt is from Programming Flex 3. If you want to try your hand at developing rich Internet applications with Adobe's Flex 3, and already have experience with frameworks such as .NET or Java, this is the ideal book to get you started. Programming Flex 3 gives you a solid understanding of Flex 3's core concepts, and valuable insight into how, why, and when to use specific Flex features. Learn to get the most from this amazing and sophisticated technology.

buy button

Copyright © 2009 O'Reilly Media, Inc.