Advertisement

AS3 101: XML – Basix

by

Well, hey there, hearty ActionScript Journeyman! You've traversed many a strange landscape so far over the last 6 months, but if all went according to plan, you emerged stronger and readier to face the next challenge. Here with the 7th installment, we'll culminate with the topic of XML.


Introduction

As we've progressed through the AS3 101 series, we've allowed ourselves to get increasingly more flexible, writing programs that responded to updates more smoothly than the previous ones. Here's where it gets truly interesting: if we can keep the data that drives the application external to the SWF, then making updates becomes a matter of editing a text file (or working with a database), rather than editing ActionScript and recompiling the SWF.

This is not anything new, but I will presume that it's new to you, and that's why you are here. I will assume you're comfortable with everything else we've covered so far in the AS3 101 series – Display List topics, Arrays, and Loops especially. But I will assume you need a formal introduction to XML itself, along with how to work with it in ActionScript.

To that end, we'll take this in three broad parts. First, we'll look at how XML is written. Next, we'll look at the techniques available to us in ActionScript 3 for working with XML. Finally, we'll put it all together to build an XML-driven product feature, a preview of which is viewable below.


Step 1: What is XML?

We have to start here. XML the acronym stands for "Extensible Markup Language," and presumably "XML" sounded cooler than "EML." A markup language is a language where markup of some sort is interspersed with regular text, giving the text structure and/or attributes. This has the effect of creating comments about the text that are distinctive from the text itself. As usual, Wikipedia has a more thorough and elegant description of Markup Languages

You are most likely familiar with one markup language: HTML (which stands for Hyper Text Markup Language). In this case, the tags (those bits that start with < and end with >) are the markup. If you know HTML, then you will have no problem picking up XML, as the syntax is identical. In fact, XML and HTML both have a shared ancestor in SGML. And XHTML is simply HTML rewritten so that it complies with XML standards (HTML had been much more loosey-goosey about certain things, which XML is not).

The markup in XML will be familiar, but this "extensible" idea might take some getting used to. The gist is that even through there are certain rules in how XML should be written, there aren't any rules on what the actual markup is. This is in contrast to HTML, where it is defined that <p> is a paragraph tag, and <a> is an anchor (or link) tag. XML defines no preset tags. You get to write your own tags, and structure them how you see fit. This lets you create an XML document that matches your data structure needs exactly. This is what is extensible about XML.

Having said that, there are several XML-based languages that do have pre-defined tags. One was already mentioned: XHTML. XHTML is just XML with things like <p> and <a> tags defined. Another XML-based language that Flash developers might care about is MXML, the markup syntax used for building Flex applications. MXML is, again, just XML, although it comes with several pre-defined tags, and trying to use a tag that doesn't have a definition results in errors. But the great thing is that once you have the hang of XML, it doesn't take much to learn XML-based languages.

For a more technical reading on the XML language, view the w3's official spec.


Step 2: An XML Document

We'll write an XML document in the first half of this tutorial, which we'll use in the second half for loading into Flash.

Open up a new text file, and save it to a location of your choosing as products.xml.

The very first thing we'll add is the XML declaration:

<?xml version="1.0" encoding="UTF-8"?>

Flash will actually work just fine without this line present. However, it's good form to make sure it's there. This is boiler plate; any editor worth its salt will have an XML template that provides this line for you.

To my knowledge, there is no other value for the version, and the encoding will typically be UTF-8. However, it's worth noting that if your text file is not UTF-8 encoded – for example, ISO-8859-1 – you might need to adjust that line (or re-encode your file to be UTF-8). Text encoding is a topic beyond the limits of this tutorial. It is a topic that should be revisited, however. A programmer lives with text files for a living, and so it's to one's advantage to be familiar with text encoding. More information than you ever thought could be mentioned about text encoding can be found here.


Step 3: All About Elements

What's an element? If you know your HTML lingo, an element is anything defined by a set of tags, such as <div>. The element includes everything from the opening tag to the matching closing tag, so the element contains both the markup (the tag) and the content of that tag. There are two key types of elements: open elements, and closed elements.

An open element comes with a matching set of tags, such as <p>...</p>, where <p> is the opening tag, and </p> is the closing tag. So, a "less than" sign (hereafter referred to as an opening angle brace) is the first character of a tag. The name of the element comes next (in our example it was "p," but remember, since XML is extensible, the name can be pretty much anything consisting of letters, numbers, hyphens, and underscores). The tag is closed with a "greater than" sign (the closing angle brace). The tag as a whole opens the element. There can be content following the opening tag, but at some point the element must close.

This is important: in HTML you may be used to being lazy about your tag closures. XML is not as forgiving. Every element that you open must be closed.

To close a node, you create another tag with the same name, only between the opening angle brace and the node name, there is a slash: </p>

Elements must be closed in the order in which they are opened. Elements can be nested, and to do that you need to open one element, and then another. When closing them, the second element gets closed first, then the first element. For example:

<a>
	<b>
		<c>
		</c>
	</b>
</a>

Notice we opened the a element, then the b element, and then the c element. When it comes time to wrap it all up, we need to close them in reverse order: c, then b, and finally a.

Note that it's customary to indent a nested element, using either a tab character or a certain number of spaces. This is for readability purposes, much like it's customary to indent the contents of a function or the body of a loop. But as in ActionScript, this whitespace is pretty much ignored, and the above could be just as legitimately be written as:

<a><b><c></c></b></a>

As mentioned at the beginning of this step, there is such a thing as an empty tag, or a self-closing tag. This is a tag that contains no content. In HTML-land, we have the image and break tags as examples:

<img src="http://activetuts.s3.amazonaws.com/tuts/044_AS3101XML/Tutorial/some.jpg" />

The syntax here is just a single tag, with a slash as the last thing before the closing angle brace. The tag opens and closes all in one step. Therefore, no content can be nested inside. The "src="http://activetuts.s3.amazonaws.com/tuts/044_AS3101XML/Tutorial/ bit isn't really content, it's an attribute. We'll get to those in a moment.


Step 4: The Root Element

Every XML document should have one, and only one, root element. This element will encapsulate all of the data in your document. Think of HTML: there is a single element that wraps up the entire thing. This is the root element.

In this tutorial, we'll make an XML product list. So, to somewhat generically refer to the entire set of data, we'll write this:

<products>
 
</products>

It doesn't really matter right now, but note that the empty second line actually has a tab character on it, for purposes of indenting the nested elements.

The important thing is to know that our entire data set will fit in between the start and end tags of this element, tabs and all. As such, it's not a bad idea to give some thought to the name of the element. We're just going to have a list of products, so "products" makes sense. But if it were to have a list of products and a list of categories, then perhaps calling the root element "products" makes less sense, as we'd probably use "products" for a nested element for the product list.

That being said, there is no rule saying you can't reuse element names. It's usually easier reading in the long run, though, if you have well-thought-out element names.


Step 5: A Product Element

Now let's start adding some data. We'll add a single product element to the document. This element will get nested inside of the products element. The element we are adding will be this:

<product>
 
</product>

Again, we are leaving room for nested elements, so that second line has a tab character. When added to the whole document, it'll look something like this:

<products>
	<product>
 
	</product>	
</products>

Step 6: Product Details

Now we can add a few details about a given product. We'll add a name, a link to the product details, and an image URL. We'll add a description in a few steps.

We'll simply add elements to the product element, and give each a value:

<products>
	<product>
		<name>Falling Snow Effect</name>
		<link>http://activeden.net/item/as3-as2-falling-snow-effect/69077?id=69077&ref=activetuts</link>
		<image>falling-snow-effect.jpg</image>
	</product>	
</products>

So we're giving the product some details. Because the <name>, <link>, and <image> elements are nested within the <product> element, they "belong" to the <product>. In a few more steps, we'll add more product elements. Each will have their own name, link, and image elements. The data structure of nesting these elements will keep each name associated with the right product.


Step 7: Entities

As it turns out the <link> node will be problematic, though. If you have an XML validator handy, go ahead and run it. You'll get an error to the effect of "expected semi-colon." I'm using TextMate for my code editing, and running the Tidy command from the XML bundle will show the following error:

TextMate's XML bundle also has a "Validate Syntax" command which will perform a similar function, but will ultimately error on the fact that there is no DTD (Document Type Declaration). Tidy, however, will "clean up" your file after a successful validation, indenting lines and remove empty lines. You may or may not appreciate that.

In fact, you can also see in the above screenshot that TextMate also highlights the ampersand as being invalid (with a solid red background).

This is actually the cause of the error, because of something called entities.

You're probably already familiar with entities from HTML, even if you aren't familiar with the name. They are pretty much the same in XML. An entity is a special code that represents a special character. For instance, in HTML you can type a regular old double-quote key and you'll be fine. But if you want to use "curly" quotes, even if you knew how to type them (Option-left-square-brace and Option-Shift-left-square-brace on the Mac), you can't be guaranteed that the XML processor that's opening the file will treat the character correctly (due to text encodings and other black magick…don't worry about why right now). Instead, you can type the entity that represents the curly braces:

&ldquo;

and

&rdquo;

For example...

&ldquo;That's what she said...&rdquo;

In fact, we could make that statement even prettier with more entities, to represent the apostrophe and the ellipses (the three periods…the right way to type that is with a single character called the ellipses. It's Option semi-colon on the Mac, by the way).

&ldquo;That&apos;s what she said&hellip;&rdquo;

It looks ugly right now, but run that through an HTML parser and you get:

“That's what she said…”

Which is subtly more readable than:

"That's what she said..."

XML has a handful of built-in entities, which basically amount to characters that have special meaning in the XML specification: <, >, ", ', and &. Note the quote characters are both "straight," not "curly."

This finally gets to the problem we're having. The ampersand is a reserved character in XML. It's reserved to indicate the start of a entity. Luckily, the ampersand entity is pre-defined, and we just need to make sure we replace the actual ampersand with its entity:

<link>http://activeden.net/item/as3-as2-falling-snow-effect/69077?id=69077&ref=activetuts</link>

The five entities defined by XML are the same as they are in HTML, but here's a quick reference:

<	&lt;
>	&gt;
"	&quo;
'	&apos;
&	&amp;

As a final note on this subject, the cool thing with XML entities is that you can define your own. We won't get into this topic, but XML allows not only single-character substitution, but even entire chunks of text. Entities can act like variables (or more accurately, constants) in this regard; rather than typing the name of an author out every time it occurs, put the name in an entity and use the entity. It's cool, but beyond the scope of our tutorial (some might argue this whole step is beyond the scope of our tutorial).

For more information on entities, visit Wikipedia.


Step 8: CDATA

As promised, we'll add a description element to the product element. But in our ideal world, we'll format that description with HTML for pretty display in Flash. But think about this for a moment…how do we separate the XML data structure from the HTML markup that's actually part of the data itself, not the structure? That is, if we wrote this:

<product>
	<name>Falling Snow Effect</name>
	<link>http://activeden.net/item/as3-as2-falling-snow-effect/69077?id=69077&ref=activetuts</link>
	<image>falling-snow-effect.jpg</image>
<strong>	<description><font size="24">AS3 / AS2 Falling snow effect</font>
<p> This is an editable flash snow effect. You can change the falling parameters in ActionScript panel without any ActionScript knowledge.  <a href="http://activeden.net/item/as3-as2-falling-snow-effect/69077">View product details</a>.</p></description></strong>
</product>

How does the XML parser know that <description> and <product> are XML, and that <font>, <br />, <p>, and <a> are actually part of the content?

(Note that I understand that the <font> tag is deprecated HTML, but that's the sort of HTML that Flash can handle, and that's our final destination, so we're using the <font> tag)

The astute among you are saying, "We could use entities." Yes, we could. It would look something like this:

&lt;description&gt;&lt;font size=&quot;24&quot;&gt;AS3 / AS2 Falling snow effect&lt;/font&gt;
&lt;p&gt; This is an editable flash snow effect. You can change the falling parameters in ActionScript panel without any ActionScript knowledge.  &lt;a href=&quot;http://activeden.net/item/as3-as2-falling-snow-effect/69077&quot;&gt;View product details&lt;/a&gt;.&lt;/p&gt;&lt;/description&gt;

And that would technically work. But remember that bit about XML being readable? I would argue the above is not readable. One or two entities…fine. All of those entities…not so much.

Fortunately, there is another way. And you probably know the name of it already because you've read the step title. It's called CDATA. CDATA is short for Character DATA, and that simply means that something specified as CDATA will be ignored as XML data. In other words, don't process the characters, even if they have meaning in XML. CDATA is a special tag, which starts like this:

<![CDATA[

and ends like this:

]]>

And all along the middle you can put in whatever you want, and the XML processor will ignore it. So we can put our HTML formatted text into XML like this:

<product>
<name>Falling Snow Effect</name>
	<link>http://activeden.net/item/as3-as2-falling-snow-effect/69077?id=69077&ref=activetuts</link>
	<image>falling-snow-effect.jpg</image>
	<description><![CDATA[<font size="24">AS3 / AS2 Falling snow effect</font>
<p> This is an editable flash snow effect. You can change the falling parameters in ActionScript panel without any ActionScript knowledge.  <a href="http://activeden.net/item/as3-as2-falling-snow-effect/69077">View product details</a>.</p>]]>
	</description>
</product>

And that is far more readable.

Note we could have used CDATA on the link element, as well. Either way, you win. Choose the method that best suits the situation. As I said, one or two entities at a time is manageable. Beyond, consider CDATA.

Wikipedia, as always, has a few more details on CDATA.


Step 9: Attributes

We need a way of internally identifying a product. We'll assign an id number to the product element. However, instead of adding another element, we'll add it as an attribute.

An attribute is just another way of associating data with a element, but instead of nesting an element inside of another element, we can attach attributes to a element directly. When you use the <img> element in HTML, you ultimately also use attributes to specify the src. XML attributes work in the same way:

<product id="42">
	…

The rules:

  • First, the attribute must be contained entirely within the opening tag of the element (or within the sole tag of an empty element)
  • There must be whitespace before the start of an attribute declaration (a single space is normal, but any number of spaces, tabs, or returns is fine. I often use whitespace to make XML more readable)
  • The name of the attribute ("id" in this case) must follow the same rules for naming as elements.
  • Immediately after the attribute name there must be an equals sign ("="). No whitespace allowed.
  • Immediately after the equals sign, there must be a pair of quotes that contain the textual data contained by the attribute.
  • Inside of the quotes, you can use most any text, but remember the special characters defined by XML. Use entities if needed. Whitespace is allowed here.
  • If a single element has more than one attribute, separate each set with whitespace, for example:
<product id="42" order="2">

The above is just for demonstration purposes. We'll be fine with just an id attribute.

When do you use attributes and when do you use elements? That's up to you. My main rule of thumb is that short data can go in attributes, and longer data can go in elements. However, I do find empty elements convenient, so I might err on the side of using attributes if possible. On the other hand, the syntax for reading attributes in ActionScript 3 is arguably (if ever so slightly) more verbose than reading elements, so if I have to use one element for length reasons, I'll tend to use elements for the rest of the data, as well.


Step 10: Replicate Some Products

Now that we have one product built up, we can create a list of them by typing out (or copying and pasting) the same structure, just with different data. A bird's eye view of the structure will look like this:

<products>
	<product>
		…
	</product>
	<product>
		…
	</product>
	<product>
		…
	</product>
	<product>
		…
	</product>
</products>

Each individual product element represents a single product, so we have four products in total. Each will have different data contained therein, but each will also follow the structure we worked out with the first product. Here's the whole document:

<?xml version="1.0" encoding="UTF-8"?>

<products>
	<product id="42">
		<name>Falling Snow Effect</name>
		<link>http://activeden.net/item/as3-as2-falling-snow-effect/69077?id=69077&ref=activetuts</link>
		<image>images/snow.jpg</image>
		<description><![CDATA[<p><font size="18">AS3 / AS2 Falling snow effect</font></p>
		<p>This is an editable flash snow effect. You can change the falling parameters in ActionScript panel without any ActionScript knowledge.</p>
		<p><a href="event:link">View product details</a></p>]]></description>
	</product>
 
	<product id="13">

		<name>Flash Banner Creator</name>
		<link>http://activeden.net/item/flash-banner-creator-and-rotator/49745?id=49745&ref=activetuts</link>
		<image>images/banners.jpg</image>
		<description><![CDATA[
			<p><font size="18">Flash Banner Creator and Rotator</font></p>
			<p>This is a very advanced tool to create professional looking dynamic flash banners, slideshows, ads and intros. You don’t need to know Flash or ActionScript to use the Flash Banner Creator and Rotator. You can create a flash banner simply by editing an XML file and adding your own assets. There are more than 10 different effects that can be applied to your images, text and swf files.</p>
			<p><a href="event:link">View product details</a></p>]]>
		</description>
	</product>
 
	<product id="75">
		<name>XML Countdown</name>
		<link>http://activeden.net/item/as3-as2-falling-snow-effect/69077?id=69077&ref=activetuts</link>
		<image>images/countdown.jpg</image>
		<description><![CDATA[
			<p><font size="18">XML Countdown</font></p>
			<p>A dynamic, customisable countdown, with universal time support: Everybody on the internet will countdown to the same moment. It’s flip-style animated: inspired by clocks on grand train stations. Modern stylish way to count down to a date up to 999 days in the future.</p>
			<p><a href="event:link">View product details</a></p>]]>
		</description>
	</product>
 
	<product id="8423">
		<name>XML Slideshow with Ken Burns Effect</name>
		<link>http://activeden.net/item/xml-slideshow-with-ken-burns-effect/32354?id=32354&ref=activetuts</link>
		<image>images/kenburns.jpg</image>
		<description><![CDATA[
			<p><font size="18">XML Slideshow with Ken Burns Effect</font></p>
			<p>MAIN FEATURES:</p>
			<ul>
				<li>Unlimited pictures</li>
				<li>Highly customizable design via XML only</li>
				<li>You can customize this slideshow in it’s smallest aspects: transitions, speeds, colors, sizes, etc, all via a well structured xml file.</li>
				<li>Awesome Ken Burns effect</li>
			</ul>
			<p>This can become the ultimate header, banner, gallery for your website.</p>
			<p><a href="event:link">View product details</a></p>]]>
		</description>
	</product>
</products>

Step 11: Turning to Flash

For the remainder of the tutorial, we'll be building a simple product feature that will use the XML document we just finished creating. It will have small buttons that represent each of the features, which can be clicked on to fill the main area with details about that product. Additionally, as a small bonus, the features will automatically advance through the list.

As usual, we will be skipping over the artwork creation process, and I advise you to use the starter file found in the download files.

If you like, you can use the following description of the starter file either as a tour of what's already there, or as a guide of what you need to build on your own, if you choose to turn down the starter file.

First, there is a background layer with a rectangle on it. No biggie.

Next, there is a dynamic TextField called caption_tf. This is styled with a larger font size, so it acts kind of like a heading. It is positioned so that a row of buttons will be above it, and an image will load below it. Both the buttons and the image will be placed programatically.

Then we have another dynamic TextField, called description_tf. This is set to a smaller font size more suitable for body copy. The field is also set to multiline and has a larger area to accommodate a bit more text. It is positioned so that it will sit below the image.

Both TextFields have fonts set for embedding, as we'll be updating the text dynamically through ActionScript.

Lastly, there is a symbol in the library called FeatureButton, and it is set to export for ActionScript. Its ActionScript class name is also FeatureButton (this name is the important one), and it is set to export in the first frame. An instance of this button is placed on stage in a guide layer called "linked symbols" for convenient editing. This layer will not be included in the SWF, however, since it is a guide layer.


Step 12: ActionScript 3, XML, and E4X

Before we get to practical code, I'd like to bring up the way ActionScript handles XML. To be specific, the way ActionScript 3 handles XML. In ActionScript 2 (and 1), XML was certainly possible, and even quite common, but the syntax involved in getting data out of XML was cumbersome. In brief, it involved a lot of usage of the "children" property and was rather easy to get lost when making your way through a complex document.

ActionScript 3 promotes XML to a "first class citizen," that is, a top-level object. Put another way, XML is so important to AS3 that it's officially a native datatype. No import lines are required to make it work, and the crazy thing is you can actually type XML right into your ActionScript files. Try this out: open up a new Flash ActionScript 3 file, and add the following into the Script panel:

var products:XML = <products>
	<product id="42" />
<products>

You can, if you like, make it the whole products XML file we created earlier, but the point I'm trying to make is that it works. I don't typically use this trick too much, but it is particularly useful when working with dummy data. That is, when you are just beginning a project, and you don't have the actual XML files ready, you can still get things working in ActionScript quickly by stubbing in some temporary XML data like this. In related news, when you need to create a small test file, copying your XML right into the ActionScript can save you from having to go through the process of loading the XML file when that's not the thing your testing.

Last but certainly not least, because XML is a first-class citizen of ActionScript 3, we have a syntax for getting data out of XML objects through something called E4X (ECMAScript for XML; ActionScript 3 is an ECMAScript-compliant language, and therefore implements E4X). E4X provides a simple dot-syntax for traversing the XML tree structure. If you've used XPath to dig into XML, it's similar to that system. We'll get into the details as we build our product feature piece, but here's a taste of E4X, using the in-ActionScript XML from two paragraphs ago:

trace(products.product.@id);

In short, this references the main XML object (products), selects the nested product element (.product), and finally the value of the id attribute of that element (.@id). The details are slightly more complicated than that, but hopefully that illustrates how smoothly ActionScript 3 can work with XML.

You can read more about E4X on Wikipedia, and about ECMAScript as well.

If you're curious, you can either fire up an AS2 Flash file and check out the XML handling, or you can actually use an AS3 port of the AS2 XML class. Look in the help docs under the flash.xml.XMLDocument. I would advise you to do this only for academics sake and to satisfy your curious appetite, as I would personally never wish the AS2 XML syntax on even my worst enemy.


Step 13: Creating a Few Variables

Let's get started. First, we're going to need some variables that will get used later on. We'll just create them now and their purpose will be explained more fully as we need to use them.

In your feature Flash file (we're done with the temporary one we created in the last step), we need to place a script on the first frame of the actions layer. In this script, which will contain all of our logic for the piece, start off by declaring and placing values into a few variables:

var xml:XML;
var featureIds:Dictionary = new Dictionary();
var link:String;
var loader:Loader = new Loader();
addChild(loader);
loader.x = 10;
loader.y = 86;

The xml and link variables will obtain values later on in the program. The featuresIds needs to be an empty Dictionary for now, which will get filled in later on, as well. The loader needs to be created and placed on the stage, but again, we won't load anything until later.


Step 14: Loading an XML file

We may be able to write XML directly into ActionScript, but it's more flexible to load it at runtime from an external XML file. This way, we can build a Flash piece that's flexible enough to respond to changes in the XML, and then performing updates becomes a matter of updating the XML file, and not the Flash file. This is the way it should work; updating XML is easy, updating Flash files is less so.

So, we need to load the XML file. We use the URLLoader object to do this. Add the following after the existing variable declarations:

var xmlLoader:URLLoader = new URLLoader();
var url:URLRequest = new URLRequest("products.xml");
xmlLoader.load(url);
xmlLoader.addEventListener(Event.COMPLETE, onXmlLoad);

First, we create a URLLoader. A URLLoader simply loads the contents of any given URL. This is different from the Loader (which we met in the AS3 101 tutorial on Display Objects), which is designed especially for loading SWFs and bitmap image files, and displaying. The URLLoader will load anything. In our case, we want to load a text file.

In the second line, we create a URLRequest object that points to the URL of the XML file we want to load. In this case, it's a relative path (and the XML file is in the same directory as the SWF). If you like, you can place the XML elsewhere, and adjust the string going into the URLRequest. It can even be an absolute path if you like. Note, however, that security restrictions arise when loading an XML file from a different domain from which the SWF resides. Dealing with this is beyond the scope of this tutorial, but it's beneficial to be aware of the situation, and to know that you can search for "crossdomain.xml" for how to deal with it.

The third line tells the URLLoader to start loading from the URLRequest.

We add an event listener on the fourth line, making sure we can act upon the loaded data once it has fully loaded. We can't do anything with the XML file before then. Also, we should set up the ProgressEvent.PROGRESS and IOErrorEvent.IO_ERROR event handlers (discussed more fully in the AS3 101 tutorial on Display Objects), but for purposes of this tutorial we will mention them, but not act upon them.

As a final part to this step, we should make sure we write (or at least start writing) the onXmlLoad function that will execute once the XML loads:

function onXmlLoad(e:Event):void {
	trace("xml loaded");
}

At this point, you should be able to run the file and see a trace in the Output panel when the XML file loads (it won't take long). Or if something went wrong, you might see an error in the Output panel, probably because of a misplaced XML file.


Step 15: Creating XML

Now that we have the xml file loaded, we need to turn the text data into an xml object. This is easily accomplished by adding the following line to the onXmlLoad function:

function onXmlLoad(e:Event):void {
	trace("xml loaded");
 <strong>xml = new XML(xmlLoader.data);</strong>
}

We're using the variable that we created a few steps ago. It exists outside of the function so that it persists for the duration of the Flash application. Now we get to use it.

The URLLoader's data property is simply the contents of the file we loaded. In this case, it's text data, which happens to be XML data, and passing an XML-formatted String to the XML constructor (new XML()) will parse that string into an XML object. So now we have a proper XML object in the xml variable. You can test it out with the following line at the end of the onXmlLoad function:

trace(xml.toXMLString())

If you test now, you should see the XML document, more or less, get printed to the Output panel.


Step 16: Parsing XML

Now for some E4X magic. We'll get lots of experience with this, but right now we need to get all four (or however many) product elements out of the XML so we can build the four (or however many) buttons. Using E4X expressions will be as "greedy" as they can, meaning they will grab as many matches as are valid. This works to our advantage, as we can write a very simple expression and have it grab all product elements.

At this point, the variable xml holds the entirety of the XML object. As such, referring to the xml variable means we're referring to the root element of the XML, that is, the element. From there, we can use dot-syntax expressions to drill down into the data structure. If we make the following the next line at the end of the onXmlLoad function:

var productElements:XMLList = xml.product;

We'll get all elements that are direct children of the root element. We'll see later how we can be more selective. Right now, it's important to note that an E4X expression actually returns an XMLList object, not an XML object. An XMLList object is a somewhat quirky object, and can behave like an XML object in several situations. You can think of it like an Array of XML elements. In our case, it will have individual entries for each of the four elements.

In fact, we can explore the XMLList a bit before going further. Try the following:

trace(productElements[2]);

And you should see the third element in the Output panel (the indices, like Array indices and depths for DisplayObjects, are 0-based).

You can also find out how many items are in the list by using the length() method:

trace(productElements.length());

It's important to realize that XMLLists are similar to Arrays but not actually Arrays. Something that tends to trip me up often is that I forget that Arrays have a myArray.length), and XMLLists have a myList.length()). It's a necessary distinction because XMLLists are capable of processing E4X expressions, so writing myList.length would actually look for elements contained within the list.

We can put these two bits of information together and loop over the XMLList (deleting the previous traces):

var len:int = productElements.length();
for (var i:int = 0; i < len; i++) {
	trace("Element " + i);
	trace(productElements[i].toXMLString());
}

We're still just tracing, but we can now loop over the data, much like we did back in the looping tutorial. Only now our data is external to out Flash application. How cool is that?


Step 17: Building the Buttons

OK, we're looping. Let's do something with the data.

As we loop, we'll create a new button from the library symbol, position it, and add to the display list. We'll also give it a name (for convenience) and assign a CLICK event listener.

var len:int = productElements.length();
var btn:FeatureButton;
for (var i:int = 0; i < len; i++) {
	btn = new FeatureButton();
	btn.x = 10 + i * 50;
	btn.y = 10;
	addChild(btn);
	btn.name = "btn" + i;
	btn.buttonMode = true;
	btn.addEventListener(MouseEvent.CLICK, onButtonClick);
}

This isn't anything new from what we've covered in previous AS3 101 tutorials. The buttonMode property might need to be explained: by default a Sprite or MovieClip with button-like behavior does not display any special cursor. On the web, however, we're used to a finger cursor when rolling over something clickable, so it's nice to turn buttonMode on, which does exactly that: the cursor turns into a finger cursor when the Sprite is rolled over.

Also, stub out the event handling function:

function onButtonClick(me:MouseEvent):void {
	trace("button clicked: " + me.target.name);
}

If you test it now, you should see four buttons show up, each, when clicked, will trace out its name.

Now, for the really cool part. Edit your XML document; either delete a element (so that you have 3) or add an extra one (so that you have 5). When you test your SWF again (just republish it in Flash), you should that the number of buttons matches the number of elements. Admit it, that's cool.


Step 18: Button Clicks

Now we need to handle the click more elegantly. What we're going to do is associate each element's id attribute with a FeatureButton instance. We'll use the Dictionary (featureIds) we set up earlier.

Now, in the loop, we need to get the id from the XML element, and then store it in the Dictionary along with the FeatureButton.

for (var i:int = 0; i < len; i++) {
	btn = new FeatureButton();
	btn.x = 10 + i * 30;
	btn.y = 10;
	addChild(btn);
	btn.name = "btn" + i;
	btn.addEventListener(MouseEvent.CLICK, onButtonClick);
 
	var productElement:XML = productElements[i];
	var idString:String = productElement.@id;
	var id:int = parseInt(idString);
	featureIds[btn] = id;
}

The steps are broken down. In the first (new) line, we simple get a reference to the product element that we are currently looping with. Next, we use E4X to grab the id attribute out of that element (remember, the "@" symbol gets us into a element's attributes). Now, because XML data is always a String, and our ids are actually integers, we need to convert from a String to an int, which is what the parseInt function does.

This last point is important to remember: just because you know its a number (or boolean, or date, or whatever), doesn't mean that ActionScript recognizes it as such. XML is really just text data, so everything coming out of it through an E4X expression will be a String. If you want it to be something else, you need to explicitly convert it.

The final line makes the connection between a button and its id by using the button as a key in the Dictionary, and assigning the id to be the value.


Step 19: Button Clicks, Part 2

Next, we need to get that id back out once a click occurs. In the onButtonClick function:

function onButtonClick(me:MouseEvent):void {
	trace("button clicked: " + featureIds[me.target]);
}

If you try this out, you should see that you get the matching id tracing for each click.

OK, with that working, we can now proceed to get the rest of the product data. Here's where E4X goes from cool to powerful. We're going to selectively choose a product element out of our whole XML document based on this id. This not unlike querying a database for that one row with a certain id, out of hundreds of rows. It looks like this:

function onButtonClick(me:MouseEvent):void {
	var id:int = featureIds[me.target];
	trace("button clicked: " + id);
	var productElement:XML = xml.product.(@id==id)[0];
}

There are a few tricks going on at the same time, actually. The first is the part that looks like this: (@id==id). The parentheses allow for a conditional selection of XML elements. If we had just written xml.product.@id, we'd get an XMLList of all of the id attributes contained in the product elements. However, with the conditional expression added in, E4X finds elements bases on the criteria we set in the parentheses. In this case, we're looking at the element's id attribute and comparing it against our known id from the Dictionary.

The second trick involves skipping the XMLList (in a manner of speaking). You'll recall that an E4X expression returns an XMLList, not an XML object. Well, we can be reasonably certain that each product element has a unique id (how? Well, let's just assume that we have some mechanism in place to assure this, much like how databases have ways of ensuring unique keys for table entries). Therefore, each time this expression is run, it will presumably return an XMLList consisting of a single XML object. Even if it didn't, we'd still only want one so that we can proceed with displaying the data.

So, we add that [0] at the end of the expression. We could have written it like this:

var productElementMatch:XMLList = xml.product.(@id==id);
var productElement:XML = productElementMatch[0];

Which, no doubt, there will be dissenting comments complaining that I'm evangelizing terrible technique. In certain situations, you might prefer the more verbose (two-line) approach. In others, you may prefer the shorthand technique to save yourself the line and the extra variable creation.

Why show you this? There are two reasons why I'm showing you this. One is, simply put, to show you this. If no one ever tells you these subtle details of the ActionScript language, how will you ever know? The other is that I'm reasonably confident that our ids will be unique, and I would prefer the less verbose version of the code.


Step 20: Button Clicks, Part 3

Finally, we can do something with this data. From here, it's actually rather simple. We do a few more simple E4X expressions to get the individual data out of the product element, and we use that to populate the content of the feature area. Here's the complete onButtonClick function:

function onButtonClick(me:MouseEvent):void {
	var id:int = featureIds[me.target];
	trace("button clicked: " + featureIds[me.target]);
	var productElement:XML = xml.product.(@id==id);

	caption_tf.text = productElement.name.toString();
	description_tf.htmlText = productElement.description.toString();
	link = productElement.link.toString();
	loader.load(new URLRequest(productElement.image.toString()));
}

The first two lines are fairly simple; they just take strings from the XML and place them into the two TextFields. The one variation is with the description TextField. With that one, we're using the htmlText property instead of the plain old text property. Doing so lets Flash render a simple set of HTML tags for text formatting purposes. These are the tags that we inserted into the <description> element, that required the use of the CDATA tag.

We're also simply placing the string from the XML's <link> element into the link variable we created a while back. We'll use this in the next step, when we hook up the link in the description text field.

Lastly, we again just using the String value of the <image> element to load something into the Loader we created at the same time as the link variable. The String URL needs to be wrapped up in a URLRequest, just like it did when we loaded the XML document. This time, though, we're doing that and loading the file all in one step. It's a bit more obtuse, but you'll see this style often.

If you test it out now, it should be looking pretty good!


Step 21: Handling a Text Link

We have <a> tags in our descriptions, but they don't do much. Here we'll introduce another technique, the TextEvent.

When a TextField has HTML text in it, and that HTML has an <a> tag, it automatically becomes clickable. And if the value of the href attribute is a URL, you'll be taken to that page in your web browser when you click the link. But ActionScript provides a little more than a plain hyperlink; you can use the link to trigger ActionScript as well.

First, the value of the href attribute needs to start with "event:" as opposed to "http://" or being a relative link. Useing the event psuedo protocol, we can get Flash to treat the click a little differently. We've already done this in the XML document, but I mention here as a necessary step.

Second, we need to add an event listener to the TextField in question:

description_tf.addEventListener(TextEvent.LINK, onTextLink);

This is the event that is dispatched when the href starts with "event:"

Finally, we need to write that function. You'll receive a TextEvent object in the function parameters, which will have the value of the href in the text property. We're using a filler value, "link." In a more complex application, this could be data that needs to be acted upon, or an identifier that differentiates between one link or another. We simply want the text event, then we'll use the value of the link variable to hop off to a new page. To see what happens, though, try the following:

function onTextLink(te:TextEvent):void {
	trace(te.text);
}

Now, to wrap this up, replace that trace with the following:

function onTextLink(te:TextEvent):void {

	navigateToURL(new URLRequest(link));
    
}

Test the movie, and you should be able to navigate using the "View product details" link.


Step 22: Creating an Initial State

Right now, everything works, except that the page is pretty empty when it first loads. Everything is great after you click on a button, but we should probably make the first product show up by default. This is easily accomplished by simulating a button click on the first button once the XML has loaded.

At the end of the loop in the onXmlLoad function, add the following code:

if (i == 0) {
	btn.dispatchEvent(new MouseEvent(MouseEvent.CLICK));
}

The whole function should now look like this:

function onXmlLoad(e:Event):void {
	trace("xml loaded");
	xml = new XML(xmlLoader.data);
	//trace(xml.toXMLString())
	var productElements:XMLList = xml.product;
	//trace(productElements[2])
	//trace(productElements.length());
	var len:int = productElements.length();
	var btn:FeatureButton;
	for (var i:int = 0; i < len; i++) {
		btn = new FeatureButton();
		btn.x = 10 + i * 50;
		btn.y = 10;
		addChild(btn);
		btn.name = "btn" + i;
		btn.buttonMode = true;
		btn.addEventListener(MouseEvent.CLICK, onButtonClick);
 
		var productElement:XML = productElements[i];
		var idString:String = productElement.@id;
		var id:int = parseInt(idString);
 
		featureIds[btn] = id;
 
		if (i == 0) {
			btn.dispatchEvent(new MouseEvent(MouseEvent.CLICK));
		}
	}
}

This is pretty much the same as clicking on the first button (this code only runs if i is 0, in other words, the first time through the loop when we create the first button). The difference is that the CLICK event is not generating through an actual mouse click by the user, but instead by manually calling dispatchEvent on the button MovieClip. This dispatchEvent call would happen internally whenever a real click happens. This isn't an appropriate technique in all cases, but here it's fine; if you run the movie now, you'll see the first feature show up automatically.


Step 23: Bonus Step: Styling the Text Field

If you know HTML, you probably also know CSS. Just as Flash supports a limited set of HTML in its TextFields, it also supports a limited set of CSS for styling HTML text in its TextFields. This step isn't necessarily related to the use of XML, but we'll cleverly incorporate some baked-in XML to deliver some styles to our text field, and in the process learn the basics of how to style dynamic HTML text with CSS.

First, we need to write our style sheet. All of the following code can easily be placed at the end of your script. We can create an inline XML variable (that is, one embedded in the script as opposed to loading an external document), and then put our CSS in a CDATA tag:

var css:XML = <style>
	<![CDATA[
		a {
			color:#992408;
		}
	]]>
</style>

If you want to see that this works, you can follow this with a trace:

trace(css.toString());

Next, we can create a StyleSheet object:

var styles:StyleSheet = new StyleSheet();

And then we need to parse the CSS "file" text into the StyleSheet, like so:

styles.parseCSS(css.toString());

This takes a string that contains CSS code and makes it usable as an ActionScript StyleSheet object. Now we can apply the StyleSheet to the description TextField:

description_tf.styleSheet = stylesheet;

And if you test it now, you should see your text links show up in a redish color:

Note that parseCSS takes any string, and we could have just done this:

styles.parseCSS("a {color:#992408;}");

For a simple style, this is probably workable, but as you likely know, CSS documents can get lengthy, and the use of whitespace (newlines and indentation) makes it far easier to work with the file. What we're doing here is using ActionScript's ability to write XML data right into a script, and using XML's CDATA to produce a string that is basically code within code (if you count the XML as code, too, then it's code within code within code).

This technique makes it really easy to deal with the escaping of quotes or newlines. Easy, as in, you don't have to do it. Using inline XML with CDATA is also useful for writing JavaScript functions that you execute with ExternalInterface (a technique we'll mention but not get into here). XML is for more than just data storage!

Note that you can also load CSS files, in the same way that you load XML files. It was more concise to hard-code the CSS into the ActionScript, plus it allowed for a tangent in the use of XML. Search for " load CSS with ActionScript " for some basic instructions on using external CSS files. You'll see some similarities between that and loading external XML files.


Step 24: Conclusion

At this point, you should have an extremely powerful tool in your arsenal. XML is an extremely popular format for storing data in Flash applications. There are other methods which have their own advantages and disadvantages, but the concepts remain the same. Learning to control your Flash pieces with external assets and data officially brings you up from the beginner level and into the intermediate level.

Yes, it's true. If you've followed along with all seven parts of this series so far, you probably don't really qualify as a beginner anymore. You've learned about variables, functions, conditional logic, looping and arrays, the Flash display list, and finally XML as an external data source. At this point, the key concepts have been presented and illustrated, and while there are certainly more details to know and more advanced techniques to be learned, you should now have the knowledge necessary to handle somewhat complex projects. I hope you can look back to the beginning and amaze yourself at how far you've come.

What's next for AS3 101, you ask? I think it's time to kick it up a notch. Look for some tutorials on the basics of Object-Oriented Programming to hit soon. We'll still come back to "essential" topics from time to time, though, so don't worry if you still feel there are some holes left in the foundation.

Advertisement