< Day Day Up > |
Flash support for XML is manifested through several objects and methods, primarily the XML object. This object loads XML, transforms XML to an ActionScript datatype, and provides methods to manipulate the data. The XML object also provides the ability to send XML to a server. This is particularly useful when submitting data to the server requires secure transmission, as in passwords, or when a server-side script is expecting XML. XML enables us to read and write to a server without any additional data access components (see Chapter 14, "Using Client-Side Data Integration," for more information). Reading an XML DocumentXML provides a structured view of data that can be read by a Flash application on the Internet. The XML object has a method to load XML documents from a server. The Flash Player supports standard XML and has built-in parsing capability. Note that the XML document has to be well- formed for accurate data parsing. In this part of the chapter, we build a Flash navigation system for a web application using an XML file to provide us with the menu item names and links. In this simple example, we do the following:
The menu will consist of a movie clip that exists in the Library, a custom XML object that will load and parse the data, and a method to populate the menu. Figure 12.3 shows the final menu. The menu is created based on the number of elements in the XML document. Figure 12.3. XML-driven menu.
The XML document used to create this menu is shown in Listing 12.7. The document root is the first node of the XML content. Notice the attributes of each root-child node. Each node contains a " name " and an "action" attribute. These will be used to populate menu items as they are created. Listing 12.7. Contents of XMLMenu.xml <menu>// document root <item name="Home" action="/home.htm"/>// child node with attributes <item name="Products" action=http://www.amazon.com/product.htm&id=87/> <item name="Solutions" action="/solution.htm"/> <item name="Customers" action="/customers.htm"/> <item name="About Us" action="/aboutUs.htm"/> </menu> Loading XML DataThe first step to creating our XML-driven menu is to load the XML document into Flash. Using the XML Object constructor, create an instance of the XML object. Listing 12.8 shows the correct syntax for creating an instance of the XML class. Listing 12.8. Creating an Instance of the XML Classvar xmlInstance = new XML(); xmlInstance.load("http://www.mySite.com/XMLMenu.xml"); A better choice would be to create a custom class that inherits from the XML object. This enables us to encapsulate the file loading, parsing, and data manipulation in one class. Listing 12.9 creates a custom class named XMLMenu . This class is a subclass of the XML object. Notice that the constructor calls super() . In Chapter 5, "The Meek Shall Inherit the Earth," we learned that calling super() in the constructor of a class will call the constructor of its direct ancestor . In this case, it is equivalent to new XML() . Listing 12.9. Creating a Custom XML Classclass XMLMenu extends XML{ public function XMLMenu(XMLFileName:String) { super(); this.load(XMLFileName); } } The custom class constructor will load the XML file when the instance is created and passed a file name. Remember that when a subclass is created, the new class inherits all the properties and methods of the ancestor, in this case, the XML object. Therefore, we can call the load method of our custom class just as we would with the native XML class shown in Listing 12.8. The instance is created in the host file that uses the XML content. In the case of the XML menu, that will be the host FLA file, XMLMenu.fla. Figure 12.4 shows the new instantiation code in the first frame of the main timeline of XMLMenu.fla. Figure 12.4. Creating an instance of a custom XML class.
The next step is to parse the data. Because there might be some latency between the file request and the file load, the XML object has an event that is fired when the file has finished loading. onLoad() is a method added to the custom class to capture file load completion. Listing 12.10 includes the onLoad() method that is executed when the file has been completely loaded into the object. In this method, we can parse the data and notify any interested objects when the data is loaded. This is also referred to as a callback method. Notice that the onLoad method contains a Boolean argument. If the file loaded successfully, the argument is true; otherwise , it is false. This allows for file access error handling. Listing 12.10. Adding the onLoad() Method to the Custom XML Classclass XMLMenu extends XML{ public function XMLMenu(XMLFileName:String) { super(); this.load(XMLFileName); } = public function onLoad(success:Boolean){ if (success) { trace("file loaded"); //parse data if needed //notify display objects that data has loaded }else{ trace("FILE NOT LOADED"); //handle file load error } } } Parsing the DataHow you parse the data is dependent on what you are going to do with the data. In the case of the Flash menu, the attributes needed to build the menu are the name and action for each menu item. As we traverse the XML document, an array can be built and passed to the main timeline to build the menu.
In Flash, XML objects have a hierarchical tree structure. There are many methods and properties we can use to traverse the tree structure and extract the data. All XML objects begin with a root node. The root node can contain information called metadata, which describes the contents of the XML document. Each node has a reference to its data as child nodes. Child nodes are accessed using the childNodes property that returns a collection of nodes, as shown in Figures 12.5 and 12.6. Note that XML trees can be very deep. Each node can have children, and these child nodes can have children, and so on. Figure 12.5. XML child nodes.
Figure 12.6. XML tree nodes.
The XML class provides methods and properties to facilitate traversing data both up and down the XML tree. It does this by providing both built-in references to neighboring elements and general tree structures. Table 12.1 enumerates the properties and methods that can be used while parsing the XML object. Table 12.1. Properties, Methods, and Return Types of the XML Object
Listing 12.11 demonstrates the onLoad() method parsing the XML content. The onLoad() method employs XML properties and methods to parse the data. Notice that in the onLoad() method, the childNodes property is accessed twice. The first access points to the first node in the object. In this case, the first node is the root of the document labeled "menu," as seen in Listing 12.8. Listing 12.11. onLoad() Method Parsing the XML Contentclass XMLMenu extends XML{ public var menuItems:Array//;//create array to pass back public var parent:Object//;// create reference to calling object public function XMLMenu(XMLFileName:String, parent:Object) { super(); this.parent = parent; this.menuItems = new Array();//// instantiate menu array this.load(XMLFileName); } public function onLoad(success){ if (success) { var rootList:Object = this.childNodes//;//access root nodes var itemList:Object = rootList.childNodes//;//access child nodes var itemListLen:Number = itemList.length; for (var i = 0; i < itemListLen; i++) { var itemObj:Object = {}; itemObj.name = itemList[i].attributes.name;//// attributes itemObj.action = itemList[i].attributes.action; //// attributes this.menuItems.push(itemObj); //// populate menu array } //send menu array back to the calling fla } else { trace("Menu content failed to load"); } } } Populating the Menu with the Parsed XML DataAfter the data is parsed into suitable ActionScript datatypes (which, in this case, is an array), the menu can be populated . The calling script in the Flash file will be notified when the data has been parsed. To facilitate communication from the host to the XML object, a reference to the parent object was passed in at the time of instantiation. This enables us to call back to the parent when the data is ready. In Listing 12.12, the callback to the Flash file is included. Using the parent reference, a callback to the parent's loadMenu() method is made, passing back the menu items derived from the XML content. Listing 12.12. onLoad() Method Returning Data to Parentclass XMLMenu extends XML{ public var menuItems:Array;// create array to pass back public var parent:Object;// create reference to calling object public function XMLMenu(XMLFileName:String, parent:Object) { super(); this.parent = parent; this.menuItems = new Array();// instantiate menu array this.load(XMLFileName); } public function onLoad(success){ if (success) { var rootList:Object = this.childNodes;//access root nodes var itemList:Object = rootList.childNodes;//child nodes var itemListLen:Number = itemList.length; for (var i = 0; i < itemListLen; i++) { var itemObj:Object = {}; itemObj.name = itemList[i].attributes.name;// attributes itemObj.action = itemList[i].attributes.action;// attributes this.menuItems.push(itemObj); // populate menu array } parent.loadMenu(this.menuItems); } else { trace("Menu content failed to load"); } } } In the parent object, flashMenu.fla, the loadMenu() method exists and accepts the menuItems array from the custom XML object. Figure 12.7 shows the loadMenu() method that dynamically attaches menu items and populates their values based on the menuItems array. Figure 12.7. loadMenu() building first-level menu.
The menu we have built so far has a flat hierarchy, but most menus require additional level of depth, in the form of submenus. Because the structure of XML documents is hierarchical, or tree-like, it is suited for multi-level menus . To add submenus to our XML menu, we will add additional entries in the XMLMenu.xml file. Listing 12.13 shows the new XML that we will use to populate the menu. Listing 12.13. XMLMenu.as with Submenu Entries<menu > <item name="Home" action="/home.htm"> </item> <item name="Products" action="none"> <item name="Toys" action="/toys.htm"></item> sub menu <item name="Electronics" action="/electronics.htm"></item> sub menu <item name="Books" action="/books.htm"></item> sub menu <item name="Pets" action="/pets.htm"></item> sub menu </item> <item name="Solutions" action="/solution.htm"> </item> <item name="Customers" action="/customers.htm"> <item name="FAO Schwarz" action="/toys.htm"></item> <item name="Best Buy" action="/electronics.htm"></item> <item name="Home Depot" action="/books.htm"></item> <item name="PetCo" action="/pets.htm"></item> <item name="TVGuide" action="/pets.htm"></item> <item name="Macromedia" action="/pets.htm"></item> </item> <item name="About Us" action="/aboutUs.htm"> <item name="Corporate" action="/Corporate.htm"></item> <item name="Opportunities" action="/Opportunities.htm"> </item> <item name="Contact Us" action="/Contact.htm"></item> </item> </menu> Using what we already know about traversing an XML document, we will load and parse the document by stepping down into the submenu level of the XML. Listing 12.14 shows the new onLoad() method of the XMLMenu.as. The onLoad() method will now build an array called sub[] that will hold the attributes of the submenus, if they exist. Listing 12.14. onLoad() Method Parsing Submenu Itemspublic function onLoad(success){ if (success) { var itemList:Object = this.childNodes[0].childNodes; var itemListLen:Number = itemList.length; for (var i = 0; i < itemListLen; i++) { var subList:Object = itemList[i].childNodes; var subItemLen:Number = subList.length; var itemObj:Object = {}; itemObj.sub = []; // create sub menu array itemObj.name = itemList[i].attributes.name; itemObj.action = itemList[i].attributes.action; //iterate through current items child nodes for (var j = 0; j < subItemLen; j++) { var subItemObj:Object = {}; // create sub menu object subItemObj.name = subList[j].attributes.name; subItemObj.action = subList[j].attributes.action; itemObj.sub.push(subItemObj); // populate sub menu array } this.menuItems.push(itemObj); } parent.loadMenu(this.menuItems); } else { trace("Menu content failed to load"); } } We will also have to modify the menuItem.as file to paint the submenu when it exists. Listing 12.15 show the new populateMenu() method. If the sub[] is populated, the menu item will create a submenu. Note that this could also be done using a recursive function, allowing a menu to be of any depth, but for the sake of clarity, we have chosen a more verbose coding style, thus limiting the menu to two levels. Listing 12.15. populateMenuItem() Creating a Submenupublic function populateItem(menuItem:Object) { if (menuItem.sub.length > 0){ //sub menu exists? this.subMenuItems = menuItem.sub; this.subMenu = true; this.createEmptyMovieClip("sub_mc", 100,{_x:0,_y:0}); var xmlObj = this.subMenuItems; var subMenuItemCount:Number = xmlObj.length; var itemX:Number =0; var itemY:Number = this._height; var rowH:Number = this._height; for(var i=0; i<subMenuItemCount; i++){ // populate sub menu this.sub_mc.attachMovie("MenuItem","subMenu"+i, I); this.sub_mc["subMenu"+I]._x = itemX; this.sub_mc["subMenu"+I]._y = itemY; this.sub_mc["subMenu"+i].populateItem(xmlObj[i]); itemY += rowH; } this.sub_mc._visible = false; } this.menuItem = menuItem; this.label_txt.text = menuItem.name; this.link_str = menuItem.action; } The new menu now has submenus, and the menu content can be changed at any time without changing the Flash files. By simply changing the XML files, a completely different menu can be generated. Figure 12.8 shows the new menu with the expanded submenu. Figure 12.8. XML-driven menu with submenu.
Sending an XML DocumentThere are several ways to send data from a Flash movie to a server, and each has advantages and disadvantages. Using the getURL() method of the MovieClip object, we can append data to the URL. Using the query string on the end of the URL has length limitations and can become very unruly with large datasets. It also has security limitations. Flash Remoting and Web Services also enable us to pass data back to a server from Flash. However, these involve server-side technologies and require an investment in software and resources. Using XML documents to pass data from Flash to a server is cheap and can provide a level of data security with minimal investment in software and IT resources. Most application servers, such as ASP.NET and ColdFusion, can parse XML on the server. Many databases can also parse and insert XML data directly into tables. The XML object has methods to support the creation and packaging of XML data to be sent to a server. Table 12.2 enumerates the methods available to create and manipulate an XML object for sending data to a server. Table 12.2. XML Methods for Creating and Sending Data
send() MethodThe send() method enables us to send XML data to a URL. It is a method of the XML object and takes a single argument, a URL. Listing 12.16 shows an example of the send() method in action. This method is used when no return response is required. For example, a site that is doing a public poll or a survey might want the user to answer a few questions and submit the answers. A simple way to do this in Flash is to create an XML object to hold the answers and then submit them to a server using the XML.send() method. Listing 12.16 lists an example of building the XML object and calling the send method. Listing 12.16. XMLSendSurvey ”an XML Subclassclass XMLSendSurvey extends XML{ public function XMLSendSurvey() { super(); } public function sendSurveyResults(surveyAnswers:Array){ for (var i=0; i<surveyAnswers.length; i++){ //Create elements for each question var newElement = this.createElement("item"); newElement.attributes.answer = surveyAnswers[i].answer; newElement.attributes.question = surveyAnswers[i].question; //append element for each question this.appendChild(newElement); } //send the XML this.send("http://www.mySite.com/survey.cfm" ); } } The XML being sent to the server would contain questions and answers similar to Listing 12.17. Listing 12.17. Survey Data XML<survey> <item question="1" answer="yes" /> <item question="2" answer="no" /> <item question="3" answer="maybe" /> </survey> sendAndLoad() MethodThe sendAndLoad() method enables us to send XML data to a URL. It also enables us to identify a client XML object that will receive any returning data. The object receiving the data is an XML object created through the XML constructor method. Listing 12.18 is an example of a sendAndLoad() method call for a registration system that returns confirmation to the client. Notice that we are using the same XML object to send the registration as we are to receive the response from the server. Listing 12.18. sendAndLoad() for the Registration Systemclass XMLRegistration extends XML{ public function XMLRegistration() { super(); } public function sendRegistration(formData:Array){ for (var i=0; i<surveyAnswers.length; i++){ var newElement = this.createElement("question"+i); newElement.attributes.label = formData[i].label; newElement.attributes.value = formData[i].value; this.appendChild(newElement); } //Send the registration to the server, //pass a reference to 'this'. this.onLoad()-receives the reply this.sendAndLoad("http://www.mySite.com/registration.cfm", this); } function onLoad(success){ if (success){ //parse the reply and notify the user } } } The sendAndLoad() method encodes the specified XML object into an XML document, sends it to the specified URL using the POST method, downloads the server's response, and then loads that response into the targetXMLobject specified in the parameters. The server response is loaded in the same manner used by the load() method. When sendAndLoad() is executed, the XML object property loaded is set to false . When the XML data finishes downloading, the loaded property is set to true , and the onLoad() method is invoked. The XML data is not parsed until it is completely downloaded. If the receiving XML object previously contained any XML trees, they are discarded. Sandbox RestrictionsFor SWF files running in a version of the Player earlier than Flash Player 7, the receiving URL must be in the same superdomain as the SWF file that is issuing this call. For example, an SWF file at www.Yahoo.com can load variables from an SWF file at products.Yahoo.com because both files are in the same superdomain of Yahoo.com . If the SWF is running in Flash Player 7 or later, the receiving URL must be in exactly the same domain. For example, an SWF file at www.Yahoo.com can load variables only from SWF files that are located at www.Yahoo.com . If you want to load variables from a different domain, you can place a cross-domain policy file on the server hosting the SWF file that is being accessed. For more information, see Chapter 13. |
< Day Day Up > |