Tying It All Together

 <  Day Day Up  >  

With the class files for our core entities of the system built, all that remains is to assemble them into a single cohesive application. This will be done by creating a series of movie clips, which will act as the user 's view to the application. The individual class files will act as the controllers, while the DataAccessObject class will be responsible for the model. The code representing the views for each screen will be located in class files associated with each movie clip. There are several benefits to moving the code from the timelines to independent class files.

Movie Clip Architecture

To begin, we need to understand how the various movie clips will interact with each other. We'll be architecting the movie clips to start with a single movie (main.fla), which will act as the overarching controller, loading and unloading other movies as needed. This first movie will create empty movie clips into which the various components can be loaded. There are three empty movie clips that will be used throughout: Login_mc is a clip that holds the login screen; nav_mc holds clips that have the navigational structure; and body_mc holds the core of each screen.

Building the Classes

To provide some necessary framework for each class file associated with a movie clip, a superclass named ApplicationView will be created. The class definition for ApplicationView can be seen in Listing 16.13.

Listing 16.13. ApplicationView Acts as the Superclass to All the Classes Acting as a View
 class ApplicationView extends MovieClip{       public function ApplicationView(){}       public function onEnterFrame(){             postInit();       }       public function postInit(){             this.onEnterFrame = null;       } } 

The value of the ApplicationView class might not be obvious at first glance. It is set to inherit from the MovieClip class, contains an empty constructor, and two methods : onEnterFrame() and postInit() . onEnterFrame() is an event handler, which is called at the frame rate of a movie. We are using it to work around synchronicity issues with Flash.

When a class is associated with a MovieClip , the entire class is parsed before any visual elements are rendered on the Stage. This can lead to issues when the code is trying to address those elements. If they have not yet been rendered, they will not yet exist, and therefore the code in the class will have no effect. By adding an onEnterFrame() event, it is possible to instruct the class to execute certain commands after all the elements are rendered.

The one command in the onEnterFrame() method is a call to the postInit() method. The postInit() of ApplicationView sets the onEnterFrame event handler equal to null , effectively telling Flash not to continue to handle that event after the first time. Structurally, this enables us to create a postInit() method for each subclass, where any code relating to visual elements can be added with the safe knowledge that those elements already exist. All that needs to be remembered for each subclass is to make a call to super.postInit() so that the onEnterFrame is set to null.

The Main Movie Clip

If properly designed, main.fla will require no visual elements on the Stage except for an empty movie clip, to which our main.as class file will be attached. That class will contain the code only to implement the applications architecture. In the following sections, we examine each of the movie clips that make up the application. See Listing 16.14.

Listing 16.14. Main.as Contains the Class File That Determines How Movies Will Be Loaded and Unloaded from the Application
 class Main extends ApplicationView{       var currentUser:Employee;       var body_mc:MovieClip;       var login_mc:MovieClip;       var nav_mc:MovieClip;       function postInit(){             super.postInit();             if(currentUser == undefined){                 this.createEmptyMovieClip("login_mc", this.getNextHighestDepth());                 login_mc.loadMovie("login.swf");             } this.createEmptyMovieClip("nav_mc",this.getNextHighestDepth()); this.createEmptyMovieClip("body_mc",this.getNextHighestDepth());       }       function loadBody(mc:String):Void{           body_mc.loadMovie(mc);           body_mc._x = 200;           body_mc._y = 0;       }       function loadNav (mc:String):Void{           nav_mc.loadMovie(mc+".swf");       } } 

This class begins declaring the local variables , in this case, a variable called currentUser of type Employee ; also declared are the three MovieClip objects that will be created through code: body_mc , nav_mc , and login_mc .

This is followed by the postInit() method. As described earlier, our postInit() method will fire after all the code and visual assets are done loading. The first line of the postInit() method is a call to the super.postInit() to ensure that the onEnterFrame event handler is set to null . Next , we check to see if the currentUser property has already been populated ; if not, we load the login screen so that the end user can be authenticated to the system. The last part of postInit() creates two more empty MovieClip objects, which will ultimately hold the navigation and body of our application.

Navigation Clips (DataEntry.fla, SubManager.fla, and CustService.fla)

Each section of the application will need a navigation structure. Generally, this will consist of a series of buttons for navigating between screens. Figure 16.15 shows the navigation for the Data entry screens.

Figure 16.15. The screen consists of two buttons.

graphics/16fig15.gif

DataEntry.fla is the first of three navigation screens that will be used in the application. Each consists of a movie clip in the Library for each button. Each clip contains a text field, indicating what the link does, as well as a button. The buttons are all named the_btn . The buttons are placed on the Stage and converted into a single movie clip instance named nav . The class files are associated with the nav clip.

Listing 16.15 shows the class file associated with the nav movie in dataEntry.fla. The class files associated with these clips simply contain an onRelease handler for each button. Listing 16.15 shows the ActionScript for DE.fla. The other two navigation files closely follow this model.

Listing 16.15. ActionScript for DE.fla
 class DataEntryNav extends ApplicationView{       var create_mc:MovieClip;       var find_mc:MovieClip;       function postInit(){             super.postInit();             create_mc.the_btn.onRelease = createProduct;             find_mc.the_btn.onRelease = findProduct;       }       function createProduct(){           _parent._parent._parent._parent.loadBody ("createProduct.swf");       }       function findProduct(){           _parent._parent._parent._parent.loadBody("findProduct.swf");       } } 

This class contains no real surprises . First, the visual elements create_mc and find_mc are declared as properties. Next, the postInit() method is defined. Just like the previous one, this postInit() also begins with a call to super.postInit() . The remainder of the postInit() method associates methods of this class with onRelease events of visual objects. The button within create_mc will trigger the createProduct() method, while the button in find_mc will trigger the findProduct() method. Last, the createProduct() and findProduct() methods are defined. These simply invoke the loadBody() method of main .

The only tricky part to these two methods is understanding why the proper path to the loadBody() method is _parent._parent._parent._parent . To understand this path, it helps to add the command trace(this) as the first line of either onRelease() handler. When that trace command is added and dataEntry.swf is loaded through main.swf, the trace statement will show level0.main.nav_mc.nav.create_mc.the_btn . This shows us that the onRelease event handler is fired in the scope of the button that was clicked and released. Table 16.2 shows the paths from the_btn .

Table 16.2. The Relative Paths from the onRelease Event Handler

P ATH

D ESTINATION

_parent

create_mc

_parent._parent

nav

_parent._parent._parent

nav_mc

_parent._parent._parent._parent

main

With the data entry navigation structure in place, the next logical step is to create the pieces that will comprise the body for the data entry screens.

The Create Product Screen

As we determined in the use cases earlier, Data Entry employees have two main interactions with the system: creating products and finding products. We are next going to define the Create product screen, as shown in Figure 16.16.

Figure 16.16. The screen for product creation.

graphics/16fig16.gif

To facilitate creating a product, a Data entry screen for products is implemented. This screen is fairly straightforward, in that it contains five input text fields and an Insert button, as shown in Figure 16.16. Listing 16.16 shows the class file attached to this movie clip.

Listing 16.16. The Class Associated with Product Creation
 class CreateProduct extends ApplicationView {       var insert_pb:mx.controls.Button;       var name_txt:TextField;       var pub_txt:TextField;       var price_txt:TextField;       var desc_txt:TextField;       var issues_txt:TextField;       function postInit():Void {             super.postInit();             insert_pb.addEventListener("click", addProd);       }       function createResult(res){             trace("results");             for(var i in res){                   trace(i+":"+res[i]);             }       }       function createStatus(res){             trace("status");             for(var i in res){                   trace(i+":"+res[i]);             }       }       function addProd() {             var prod:Product = new Product(_parent.name_txt.text,  _parent.pub_txt.text, Number(_parent.price_txt.text), _parent.desc_txt.text, Number(_parent.issues_txt.text));             prod.insert(createResult,createStatus);       } } 

The code for the screen is not complicated. First, the properties are declared. These consist of a push button and five text fields. Next the postInit() method is defined, which assigns the addProd() method of the class as the handler for clicks on the push button. This is followed by the definition for the createResults() and createStatus() methods. These methods are designed to handle either results or statuses returning from the Web Services call. All these do for now is trace the results or status, but when the system is fully fleshed out, they can follow the use cases for their behaviors. Last, the addProd() method is defined. This method assembles a new Product from the user's input and calls the insert() method to send it to the server. Earlier, in the definition for the Product class, the insert method was defined to first validate the data, and when successfully validated , to pass the product data to its DataAccessObject to be persisted on the server.

The Find Product Screen

The other action that a data entry employee can take is to find a product already in the database. Figure 16.17 shows the interface that a user can use to find a product.

Figure 16.17. The product search interface.

graphics/16fig17.gif

The search interface for products has a similar look to the Product creation screen, with the addition of a DataGrid below the fields in which search results can be displayed. Other than the grid, this movie has the same five text fields and a button (see Figure 16.17), much like the previous screen.

Listing 16.17 shows the class file associated with this clip.

Listing 16.17. The FindProduct Class Is Associated to the Movie Clip in findProduct.fla
 class FindProduct extends ApplicationView{       var details_mc:MovieClip;       var name_txt:TextField       var pub_txt:TextField;       var desc_txt:TextField;       var price_txt:TextField;       var find_pb:mx.controls.Button;       var products_dg:mx.controls.DataGrid;       var view_pb:mx.controls.Button;       var edit_pb:mx.controls.Button;       function postInit():Void{             super.postInit();             find_pb.addEventListener("click",findProd);             view_pb.addEventListener("click",showDetails);             edit_pb.addEventListener("click",showDetails);             this.showHidden(false);             details_mc._visible = false;       }       function findProd(){             var propObj:Object = new Object();             if(name_txt.text.length){                   propObj.name = name_txt.text;             }             if(pub_txt.text.length){                   propObj.publisher = pub_txt.text;             }             if(desc_txt.text.length){                   propObj.description = desc_txt.text;             }             if(price_txt.text.length){                   propObj.singleIssuePrice = Number(price_txt.text);             }             Product.search(propObj,this.searchResult,this.error);       }       function showDetails(obj){             var selectedButton = obj.target.label;             if (selectedButton == "View"){                   var isEditable:Boolean = false;             } else {                   var isEditable:Boolean = true;             }             var activeProd:Object = _parent.products_dg.getItemAt(_parent.products_dg.selectedIndex);             _parent.details_mc._visible = true;             _parent.details_mc.showDetails (activeProd,isEditable);       }       function searchResult(theData){             products_dg.dataProvider=theData;             this.showHidden(true);       }       function error(status){             trace(status.description);       }       function showHidden(bool:Boolean):Void{             products_dg._visible = bool;             edit_pb._visible = bool;             view_pb._visible = bool;       } } 

This class, like the other View classes, begins by declaring the class as a subclass of ApplicationView . Next, the visual elements on the Stage are declared as properties of the class. These properties include one MovieClip (which will have its own class file), four TextFields , three Buttons , and a DataGrid . Next, the postInit() method assigns event handlers to each component. Also in postInit() is code to hide the visual elements that are not yet in use.

The postInit() method is followed by the event handlers. The first event handler method is findProd() . This method is invoked when the Find button is clicked. It checks each text field and any that are not blank are added to an object, which is sent to the server as a search strategy. When the search() method is invoked against the Product class, the method searchResult() is specified as the handler for any successful results, while the method error() is used to handle any errors.

Next, the event handler method showDetails() is defined. This method will be invoked when either the edit or view buttons are clicked. To properly determine which button has been clicked, we specify an argument to the method, obj . As we discussed in Chapter 9, the arguments to any component event handlers contain an object. One of the properties of that object is an object named target . The target object has a property label that contains the label of the button that was clicked.

Because we have two different buttons, each using this same method, we start with a conditional statement, determining which button was clicked. This condition will determine whether the details screen is loaded in a read-only or read/write fashion. The variable isEditable is created as a Boolean value to hold either true or false, determining whether the details screen will be editable. Next, the method determines which item from the DataGrid was selected and sets the selected item into a variable named activeProd . Last, the method tells the details_mc movie clip to be visible and populates it using that clip's showDetails() method. The showDetails() method can be seen in the class file for the details_mc clip.

The last few methods are fairly simple. The searchReults() method is a callback, which is invoked when data is returned from the server-side search. This simply takes the data from the server and populates the DataGrid with that data. Next, the error() method acts as an event handler for any errors returned by the server-side search. In this case, we are simply tracing the error message, but before the application is launched, a more robust error handling strategy would be implemented. Last, the showHidden() method is defined. This method is responsible for hiding or showing the DataGrid and its associated buttons. It takes a single Boolean argument and uses that to set the _visible property of the DataGrid and Buttons .

Next, we explore details_mc and the class associated with it, ProductDetails . This can be seen in Listing 16.18.

Listing 16.18. ProductDetails Is The Class Associated with the details_mc Clip in the findProduct Movie
 class ProductDetail extends ApplicationView{       var name:mx.controls.TextInput;       var pub:mx.controls.TextInput;       var issues:mx.controls.TextInput;       var price:mx.controls.TextInput;       var desc:mx.controls.TextArea;       var update:mx.controls.Button;       var prod:Product;       function postInit(){             super.postInit();             update.addEventListener("click",updateProd);       }       function showDetails(activeProduct:Object, editable:Boolean){             this.prod = Product(activeProduct);             name.text = activeProduct.name;             pub.text = activeProduct.publisher;             issues.text = activeProduct.issues;             price.text = activeProduct.price;             desc.text = activeProduct.desc             this.isEditable(editable);       }       function isEditable(bool:Boolean){             name.editable = bool;             pub.editable = bool;             issues.editable = bool;             price.editable = bool;             desc.editable = bool;             if(bool){                   update.label = "Update";             } else {                   update.label = "Return";             }       }       function updateProd(){             trace(name.text);             prod.update(name.text, pub.text, price.text, desc.text, issues.text, updateResult, updateStatus);       }       function updateResult(){ this._parent._parent.products_dg.replaceItemAt(this._parent._parent. products_dg.selectedIndex,prod);             this._parent._visible=false;       }       function updateStatus(status){             trace(status.description);       } } 

This class follows the same structure as the View classes we have already defined. It begins by extending the ApplicationView class. Next, each visual object is declared as properties. In this class, we have four TextInput components (we are using TextInputs rather than TextFields for the ease of their editable property), one TextArea , and one Button . One more property is also defined, prod , which will hold the active product.

Next, the postInit() method is defined. Other than the call to super.postInit() , this method assigns the click handler for the Button. This method is followed by the definition for the showDetails() method. In Listing 16.16, we used prodDetails_mc.showDetails() to pass the selected product from the DataGrid to the details clip. This is the method that received that message. It takes two arguments, activeProd and editable . First, we cast activeProd into a member of the Product class and store that in the class property prod . Next, the properties of the activeProd are used to populate the TextInput fields. Finally, the editable property is passed on to the isEditable() method.

The isEditable() method is defined next. This method takes a single argument, bool , which will contain a true or false value, and uses that to set the editable property of the four TextInput and one TextArea components. A conditional statement also is used to set the label on the Button to either Update or Return . Regardless of what the label property of the button says, the event handler remains the same.

Next, the updateProd() method is defined. This method acts as the event handler for the button. When invoked, it checks the label of the button that was pressed (just like the showDetails() method from the FindProduct class did). If the label reads Update , the method calls the update() method of the prod object and passes the new values from the TextInput components. It also passes a result and status method. If the label is not Update , the details screen is simply hidden again, returning the user to the search screen.

The final two methods of the class act as the result and status handler for the update() web service call. If the call returns without error, the updateResult() method will update the DataGrid on the search screen with the updated data and hide the details screen. If there are errors, the updateStatus () method will trace the error message.

The Create Offer Screen

Following the use cases, the next logical screens to work on are the screens relating to an offer. Figure 16.18 shows an Offer creation interface. This interface consists of a combo box that will be populated with products; two TextInput components to allow input for the number of issues and the price; a check box, indicating whether the offer is yet active; and an insert button.

Figure 16.18. The Offer creation interface.

graphics/16fig18.gif

These elements are combined into a movie clip, which will be associated with a class file containing all the logic for this screen. This class, CreateOffer , can be seen in Listing 16.19.

Listing 16.19. The CreateOffer class Is Associated with the Movie Clip in the Create Offer Interface
 class CreateOffer extends ApplicationView{       var prod_cb:mx.controls.ComboBox;       var numIssues:mx.controls.TextInput;       var price:mx.controls.TextInput;       var active:mx.controls.CheckBox;       var insert_pb:mx.controls.Button;       var productDAO:DataAccess;       var prodArray:Array = new Array();       function postInit(){             super.postInit();             insert_pb.addEventListener("click",makeOffer);             productDAO = new  DataAccess("http://localhost:8500/oopas2/components/product.cfc?wsdl"); productDAO.accessRemote("getAll",null,getAllResult,commError);       }       function getAllResult(res){             var num:Number = res.length;             trace(this);             trace(_parent);             trace(_parent._parent);             prod_cb.removeAll();             for (var i:Number=0;i<num;i++){                   // build product array                   prodArray[i] = new Product(res[i].NAME, res[i].PUBLISHER, res[i].SINGLEISSUEPRICE, res[i].DESCRIPTION, res[i].ISSUESPERYEAR)                   // add product to combo box _parent.prod_cb.addItem(res[i].NAME,res[i].PRODUCTID);             }       }       function makeOffer(){             var offer:Offer = new Offer( _parent.prodArray[_parent.prod_cb.selectedIndex], _parent.numIssues.text, _parent.price.text, _parent.active.selected);             offer.insert(makeOfferResult, commError);       }       function makeOfferResult(res){             trace("offer inserted");       }       function commError(status){             trace(status.description);       } } 

By now, the structure of the View classes should be very familiar. CreateOffer begins in the same way, declaring the classes properties, both those that are visual objects and those that are data properties. The properties representing visual objects in this class are a ComboBox , two TextInputs , a CheckBox , and a Button . Two other properties are also declared: prodArray , which will hold an array of products, and productDAO , which will be an instance of the DataAccess class.

Next, the postInit() method is declared. An event handler is declared for the push button here. Also implemented in this method is the creation of a DataAccess instance. As we are not already working with an instance of the Product class, we need some way to be able to retrieve all the products from the server, so a new DataAccess instance is added to this class as a property named productDAO . In the postInit() method, productDAO is initialized with the URL of the Product web service, and the accessRemote() method is used to retrieve all products. Results are routed to a callback method named getAllResult() , while any errors are routed through a method named commError() .

The next method is getAllResult() , which acts as the callback for results from the call to the server to retrieve all products. This method begins by clearing everything from the products combo box. Next, it loops over the results and makes an instance of the Product class for each item in the results. These instances are stored in the prodArray property. Still in the loop, each product is added to the combo box, using the item's name as the label and the entire object as the underlying data.

The makeOffer() method is defined next. This method is the callback for the click event on the button. When clicked, it creates a new Offer object from the user's input in the components, and it uses the insert() method of the Offer class to persist the new offer to the server.

Next, the makeOfferResult() method is defined to act as a call back for the server-side call to insert an offer. For now, the new offerID returned to the callback is traced; however, before going to production, this information would be displayed to the user in a more friendly fashion.

Finally, the commError() method is defined to handle any communications errors between the screen and the server.

The Remaining Screens

Following the model we have used for the movie clips so far (createProduct.fla, findProduct.fla, and createOffer.fla), we can easily implement the remaining use cases detailed earlier. Each screen starts with a visual layout that is converted to a movie clip, and then a class is associated with that clip.

Each class needs to begin by extending the ApplicationView class. Next, we need to declare all the visual objects contained in the movie clip, as well as any other properties the clip will need. Next, a postInit() method needs to be defined, which will associate any event handlers with visual objects, as well as establish any commands that we want to run as soon as the visual objects are set on the Stage. It is important to remember that the first line of the postInit() method should be super.postInit() so that the onEnterFrame() event handler only executes once. If we forget to add this, the postInit() method will fire at the frame rate of the movie (usually 12 times a second), which could have undesirable effects.

The final steps for each View class is to build any specific methods needed by the class. Table 16.3 shows the remaining classes to be built and the properties and methods of each class.

Table 16.3. Remaining Classes

C LASS

P ROPERTIES

M ETHODS

FindOffer

 prodArray:Array prod_cb:mx.controls.ComboBox numIssues:mx.controls.TextInput price:mx.controls.TextInput active:mx.controls.CheckBox insert_pb:mx.controls.Button productDAO:DataAccess products_dg:mx.controls.DataGrid view_pb:mx.controls.Button edit_pb:mx.controls.Button details_mc:MovieClip activeOffer:Offer 
 findProd() searchResult() getAllOfferResult() getProdResult() commError() showHidden() showProdDetails() 

OfferDetails

 prod_cb:mx.controls.ComboBox numIssues:mx.controls.TextInput price:mx.controls.TextInput active:mx.controls.CheckBox desc:mx.controls.TextArea update:mx.controls.Button 
 showDetails() isEditable() updateOffer() 

CustomerSearch

 firstName:TextField lastName:TextField street:TextField city:TextField state:mx.controls.ComboBox zipcode:TextField areaCode:TextField phoneNum:TextField stateArray:Array search:mx.controls.Button custResults:mx.controls.DataGrid edit:mx.controls.Button view:mx.controls.Button addSub:mx.controls.Button 
 findCustomer() findCustResult() insertCustomer() insertCustResult() showDetails() addSub() commError() 

Login

 user:mx.controls.TextInput pass:mx.controls.TextInput login_pb:mx.controls.Button 
 authenticate() authenticateResult() commError 

CreateSubscription

 activeCustomer:Customer prodArray:Array prod_cb:mx.controls.ComboBox offerArray:Array; offer_cb:mx.controls.ComboBox street:TextField city:TextField state:mx.controls.ComboBox stateArray:Array zipCode:TextField cancel:mx.controls.Button submit:mx.controls.Button 
 createSub() getProd() getProdResult() getOffer() getOfferResult() showVerification() commit() cancel() 

FindSubscription

 customer:mx.controls.TextInput prod_cb:mx.controls.ComboBox offer_cb:mx.controls.ComboBox prodArray:Array offerArray:Array search_pb:mx.controls.Button view_pb:mx.controls.Button 
 search() searchResult() getProd() getProdResult() getOffer() getOfferResult() viewSub() 

The complete code for the classes in Table 16.3, as well as the other classes from this chapter, can be found on the book's website at www.oopas2.com.

 <  Day Day Up  >  


Object-Oriented Programming with ActionScript 2.0
Object-Oriented Programming with ActionScript 2.0
ISBN: 0735713804
EAN: 2147483647
Year: 2004
Pages: 162

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net