Creating a Data Manager Component for All Three Applications


In the first task, you rearchitected part of the application without adding any functionality. In the second task, you added functionality while building more components. This task is like the first, in which you are rearchitecting the application without adding any visible functionality for the user.

All three applicationsDataEntry, EComm and Dashboardshare the same data. In this task, you will build an MXML component that can be thought of as a data manager. This component will provide certain types of data to all the applications when they need it. This data manager component will be different from other components you've built in this lesson in that it will not have any visible representation that a user will see. These components are referred to as nonvisual components.

The advantage of building a data manager component is that it will centralize data requests. For instance, rather than having many HTTPService requests on different application pages and components, you can centralize them in this data manager component.

1.

Create a new folder named managers under the FlexGrocer project.

Because this new component is neither a value object nor a view, a new folder is needed.

2.

Right-click the managers folder and then choose New > MXML Component. In the New MXML Component dialog box, set the filename to be CategorizedProductManager.mxml and the base component to be a UIComponent; then click Finish.

The UIComponent is the lightest-weight component you can use when creating an MXML component.

3.

Insert an <mx:Script> block into the component. Add the following three import statements that are needed for this class:

import mx.rpc.events.ResultEvent; import valueObjects.Product; import valueObjects.Category; 


These statements are needed because there will be a result event from an HTTPService, and you will also be building arrays of Products and Categories.

4.

Just below the import statements in the <mx:Script> block, create three new private variables. The first is named categorizedProducts, data typed as Object, and set equal to a new Object. The second is named aCats, data typed as an Array, and set equal to a new Array. And the last is named rawData, data typed as XML. It does not have to be set equal to anything.

private var categorizedProducts:Object = new Object(); private var aCats:Array = new Array(); private var rawData:XML; 


5.

Open the EComm.mxml file and copy the <mx:HTTPService> tag whose id is prodByCatRPC and paste it under the <mx:Script> block you just created in the CategorizedProductManager component. Change the name of the result event handler from prodHandler to prodByCategoryHandler.

This component is being built to act as a data manager for other components. So, of course, some data will be retrieved in this component.

6.

Add to the <mx:UIComponent> tag a creationComplete event and call the send() method of the prodByCatRPC HTTPService.

This is an easy piece of code to forget to add. Remember, setting up the HTTPService does not automatically call the send() method.

7.

Again from the EComm.mxml file, copy the entire prodHandler() function and paste it just above the closing <mx:Script> tag in the CategorizedProductManager component. Change the name of the function from prodHandler to prodByCategoryHandler.

With some modifications, the existing function you just copied can supply not only an array of products but also an array of categories and products by category.

8.

Remove the following three lines of code from the function:

var prodArray:Array=new Array(); prodArray.push(prod); groceryInventory=new ArrayCollection(prodArray); 


This code is no longer needed in the new function.

9.

As the first line of the function, set the rawData variable equal to event.result and cast it to XML using the 'as' operator as shown:

rawData = event.result as XML; 


When populating the Tree, a function will be called to send back this raw data.

10.

Surround the existing for each..in loop with another for each..in loop. The iterant in the new loop should be a local variable named c, data typed as XML. The loop should look in the event.result XML object for the category nodes. Change the nested for each..in loop, so instead of looking in the event.result XML object, it looks in the iterant from the outer loop, c, for product nodes:

for each (var c:XML in event.result..category){    for each (var p:XML in c..product){       var prod:Product = new Product(          Number(p.@catID),          String(p.@prodName),          Number(p.@unitID),          Number(p.@cost),          Number(p.@listPrice),          String(p.@description),          Boolean(p.@isOrganic=="Yes"),          Boolean(p.@isLowFat=="Yes"),          String(p.@imageName));    } } 


You now have a scenario in which you can build an array of Category objects between the beginnings of the for each..in loops.

11.

Between the beginnings of the for each..in loops, create a variable local to the function named category, data typed as Category. Set that equal to a new Category in which you pass two parameters. The first parameter will be the catID attribute of the c iterant, and the second will be the catName attribute of the c iterant. The catID value will need to be cast as int, and the catName value will need to be data typed as String.

var category:Category = new Category(int(c.@catID), String(c.@catName)); 


The data types of the parameter values must be data typed because the data is untyped from the XML object.

12.

Below the line of code you just created, push the category object onto the aCats array:

aCats.push(category); 


The aCats array was declared earlier and will be the data returned in a function you will build later in this task. The array holds six Category objects, each containing the catID and catName properties as shown in the following example from the debugger:

13.

Below the line of code you just created, add a new property of the categorizedProducts object using the c iterant's catID attribute in square bracket notation as the property name, and set this equal to a new Array.

categorizedProducts[c.@catID] = new Array(); 


You are building a complex data structure here, and depending upon your computer language background, you might understand it as a hashmap, or associative array. In categorizedProducts, there will be a property for each category, identified by its category ID. Each of these properties will hold an array of all the products in that category, as shown in the following example from the debugger.

You must use square bracket notation to create the property. What you are using for the property name contains a dot itself. The compiler would not understand if you used this notation: categorizedProducts.c.@catID.

14.

In the inner loop, just after where the Product object is built, push the new prod Product object on to the array you just created.

categorizedProducts[c.@catID].push(prod); 


15.

After the close of the two loops, just above the closing brace for the function, insert the following if statement:

if(this.parentDocument.categorizedProductDataLoaded != null){    this.parentDocument.categorizedProductDataLoaded(aCats); } 


You need to understand that this if statement code is a bad practice. You are reaching into the parent document and running a function, thus coupling this component to another, which was stated earlier as a bad practice.

That being said, why are you doing this? This checks to be sure that data is loaded from the <mx:HTTPService> tag. You have not yet learned the best practice way to handle this, so for now know that this does the needed job. In a later lesson, you will learn about dispatching custom events, which is the best practice way to handle this situation.

16.

Check to be sure that the function you just built appears as follows:

private function prodByCategoryHandler(event:ResultEvent):void{   rawData=event.result as XML;   for each(var c:XML in event.result..category){     var category:Category = new Category(int(c.@catID),String(c.@catName));     aCats.push(category);     categorizedProducts[c.@catID] = new Array();     for each (var p:XML in c..product){       var prod:Product = new Product(Number(p.@catID),          String(p.@prodName),          Number(p.@unitID),          Number(p.@cost),          Number(p.@listPrice),          String(p.@description),          Boolean(p.@isOrganic=="Yes"),          Boolean(p.@isLowFat=="Yes"),          String(p.@imageName));       categorizedProducts[c.@catID].push(prod);     }   }   if(this.parentDocument.categorizedProductDataLoaded != null){      this.parentDocument.categorizedProductDataLoaded(aCats);   } } 


To be sure that you still have the big picture in mind, here is a recap of the three things that this function has done:

  • Built an array named aCats that contains all the category objects.

  • Built an object named categorizedProducts that contains a property for each category, and each property contains an array of all the products for that particular category.

  • Used a not-best-practice way to be sure data is loaded. This bad practice will be corrected in the lesson on custom events.

17.

Just under the variable declarations, create a public function named getProdsForCat(), which is data typed as an Array. It should accept a parameter named catID, data typed as an int.

This is the first of three functions you will build to complete this component. All three of the functions return data to invoking pages. In this case, the function will return a set of products based on the category ID passed to the function.

This is also an example of creating public functions that will be methods that can be called after the component is instantiated on a calling page. For instance, if the component is instantiated as shown here:

<m:CategorizedProductManager /> 


you could then invoke the method as follows:

prodMgr.getProdsForCat(4); 


18.

As the single line of code in the function, return the categorized products for the catID parameter:

public function getProdsForCat(catID:int):Array{    return categorizedProducts[catID]; } 


19.

Create another public function named getCats(), data typed as an Array. In this function, return the aCats array that was built in the prodByCategoryHandler() function.

public function getCats():Array{    return aCats; } 


20.

Create another public function named getCategorizedProducts(), data typed as XML. In this function, return the rawData XML object that was built in the prodByCategoryHandler() function.

public function getCategorizedProducts():XML{    return rawData; } 


21.

Check to be sure that your component appears as follows:

<?xml version="1.0" encoding="utf-8"?> <mx:UIComponent xmlns:mx="http://www.adobe.com/2006/mxml"    creationComplete="prodByCatRPC.send()">    <mx:Script>       <![CDATA[          import mx.rpc.events.ResultEvent;          import valueObjects.Product;          import valueObjects.Category;          private var categorizedProducts:Object = new Object();          private var aCats:Array = new Array();          private var rawData:XML;          public function getProdsForCat(catID:int):Array{             return categorizedProducts[catID];          }          public function getCats():Array{             return aCats;          }          public function getCategorizedProducts():XML{             return rawData;          }          private function prodByCategoryHandler(event:ResultEvent):void{             rawData=event.result as XML;             for each (var c:XML in event.result..category){                var category:Category=new Category(int(c.@catID),String(c.@catName));                aCats.push(category);                categorizedProducts[c.@catID]=new Array();                for each (var p:XML in c..product){                   var prod:Product = new Product(                   Number(p.@catID),                   String(p.@prodName),                   Number(p.@unitID),                   Number(p.@cost),                   Number(p.@listPrice),                   String(p.@description),                   Boolean(p.@isOrganic=="Yes"),                   Boolean(p.@isLowFat=="Yes"),                   String(p.@imageName));                   categorizedProducts[c.@catID].push(prod);               }            }            if(this.parentDocument.categorizedProductDataLoaded != null){               this.parentDocument.categorizedProductDataLoaded(aCats);            }         }       ]]>    </mx:Script>    <mx:HTTPService        url="http://www.flexgrocer.com/categorizedProducts.xml"       result="prodByCategoryHandler(event)"       resultFormat="e4x"/> </mx:UIComponent> 


Now the data manager component is finished, and it's time to put it to use in the next task.




Adobe Flex 2.Training from the Source
Adobe Flex 2: Training from the Source
ISBN: 032142316X
EAN: 2147483647
Year: 2006
Pages: 225

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