<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Russ Back &#187; ArrayCollection</title>
	<atom:link href="http://www.russback.com/tags/arraycollection/feed" rel="self" type="application/rss+xml" />
	<link>http://www.russback.com</link>
	<description>Professional Web Development</description>
	<lastBuildDate>Mon, 02 Nov 2009 08:16:33 +0000</lastBuildDate>
	<generator>http://wordpress.org/?v=2.8.2</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<item>
		<title>Creating a strongly typed ArrayCollection</title>
		<link>http://www.russback.com/adobe-flex/creating-a-strongly-typed-arraycollection.html</link>
		<comments>http://www.russback.com/adobe-flex/creating-a-strongly-typed-arraycollection.html#comments</comments>
		<pubDate>Fri, 24 Jul 2009 08:04:16 +0000</pubDate>
		<dc:creator>Russ</dc:creator>
				<category><![CDATA[Adobe Flex]]></category>
		<category><![CDATA[ArrayCollection]]></category>

		<guid isPermaLink="false">http://www.russback.com/?p=489</guid>
		<description><![CDATA[I often find myself adding getting Objects from an ArrayCollection and then having to cast them as the correct Object type before I can work with them. So here's a simple way to create a strongly-typed ArrayCollection.]]></description>
			<content:encoded><![CDATA[<h2>The scenario</h2>
<p>The ArrayCollection class&#8217; addItem(), addItemAt(), getItem() and getItemAt() methods accept/return a generic Object which is great because we can use if for storing pretty much anything. But there is no way to ensure that the ArrayCollection only contains a specific class of Object, or to return an Object cast as a specific class.</p>
<p>Typically you just get your Object out of the ArrayCollection, cast it as the class you know it belongs to, and then carry on. But when you&#8217;re doing this over and over again it makes sense to override the get and add methods to work with specific Object classes.</p>
<h2>Example data</h2>
<p>To start with we need some data and for this example we have a number of &#8216;products&#8217; to work with. So for this we have a simple Product Value Object with name and price properties:</p>
<pre>package valueobjects
{
  public class Product
  {
    public var name:String;
    public var price:Number;

    /**
    * Constructor function sets up this Product object
     */
    public function Product(name:String, price:Number):void
    {
      this.name = name;
      this.price = price;
    }

  }

}</pre>
<h2>The strongly-typed ArrayCollection (ProductArrayCollection.as)</h2>
<p>Next we need our new ArrayCollection class &#8211; which I&#8217;ve called ProductArrayCollection for this example &#8211; that extends the base ArrayCollection class:</p>
<pre>package extensions
{
  import mx.collections.ArrayCollection;
  import valueobjects.Product; 

  public final class ProductArrayCollection extends ArrayCollection
  {

    /**
     * Private property used to prevent direct use of addItem and addItemAt methods
     */
    private var _itemOk:Boolean = false;

    /**
     * Constructor function simply calls the constructor function of the ArrayCollection super class
     */
    public function ProductArrayCollection(source:Array=null)
    {
      super(source);
    }</pre>
<p>The only thing to note here is a private Boolean variable called _itemOk, which is defaulted to false. We&#8217;ll use this to protect our ArrayCollection from rogue addItem() and addItemAt() method calls, as we&#8217;ll see next, starting with overrides of the addItem() and addItemAt() methods&#8230;</p>
<p>These overrides both check to see if the _itemOK property is set to true. If it&#8217;s true then we call the super class&#8217; addItem() (or addItemAt()) method which adds the supplied Object to the ArrayCollection. If it&#8217;s false, we throw an error. Doing this prevents us from calling the addItem() and addItemAt() methods directly as we can&#8217;t set the _itemOK value to false from outside the ProductArrayCollection class, we need to do that with an internal method.</p>
<pre>    /**
     * Overrides the ArrayCollection's addItem() method
     * Adds an item only if the _itemOK property has been set by the addProduct() method
     * Resets the _itemOK property if the add is successful, or throws an error
     */
    override public function addItem(item:Object):void
    {
      if (this._itemOk)
      {
        super.addItem(item);
        this._itemOk = false;
      }
      else
      {
        throw new Error(&quot;Use addProduct() to add a Product to the ProductArrayCollection&quot;);
      }
    }

    /**
     * Overrides the ArrayCollection's addItemAt() method
     * Adds an item at the specified index only if the _itemOK property has been set by the addProductAt() method
     * Resets the _itemOk property if the add is successful, or throws an error
     */
    override public function addItemAt(item:Object, index:int):void
    {
      if (this._itemOk)
      {
        super.addItemAt(item, index);
        this._itemOk = false;
      }
      else
      {
        throw new Error(&quot;Use addProductAt() to add a Product to a specific location in the ProductArrayCollection&quot;);
      }
    }</pre>
<p>To enable us to do that then, we need two new methods: addProduct() and addProductAt(). These simply set the value of _itemOK to true before calling the addItem() and addItemAt() methods:</p>
<pre>    /**
     * Sets the _itemOK property and calls the addItem() method
     */
    public function addProduct(product:Product):void
    {
      this._itemOk = true;
      this.addItem(product);
    }

    /**
     * Sets the _itemOK property and calls the addItemAt() method
     */
    public function addProductAt(product:Product, index:int):void
    {
      this._itemOk = true;
      this.addItemAt(product, index);
    }</pre>
<p>So now we can only add Product Objects to our ProductArrayCollection, the last thing we need to do is add a getProductAt() method that will return a Product rather than a generic Object:</p>
<pre>    /**
     * Uses the ArrayCollection's getItemAt() method and returns the result as a Product
     */
    public function getProductAt(index:int, prefetch:int=0):Product
    {
      return this.getItemAt(index) as Product;
    }

  }
}</pre>
<p>And that&#8217;s it &#8211; we now have a strongly-typed ArrayCollection that we can use for Products and can be extended for any other Object class.</p>
<h2>The ProductArrayCollection in action</h2>
<p>The example below is a simple one. Clicking on the each button calls the method on the button&#8217;s label. The addItem() and addItemAt() methods should should do nothing while the addProduct() and addProductAt() methods update the list. The core methods fail silently as this example is in release mode and the errors don&#8217;t display. <a href="/wp-content/examples/creating-a-strongly-typed-arraycollection/srcview/creating-a-strongly-typed-arraycollection.zip">However you can download and run the source code to generate these errors for yourself</a>.</p>
<p>Clicking on the getProductAt(0) button should display name of the name of the item at position 0 in the list.  An explanation of the source code is below and you can <a href="/wp-content/examples/creating-a-strongly-typed-arraycollection/srcview/creating-a-strongly-typed-arraycollection.zip">download and edit the source code</a>, or right-click on the demo below and choose View Source to see how it all fits together.</p>
<h3>Example</h3>
<p>
		<script type="text/javascript">
		var vars = {};    
		var params = {
			base: "."
			};
		swfobject.embedSWF("http://www.russback.com/wp-content/examples/creating-a-strongly-typed-arraycollection/main.swf", "example", "553", "450", "9.0.0", "expressInstall.swf", vars, params);
	</script>
</p>
<div class="flash_content" id="example">
<p>You need Flash Player 10 to view this content. You can <a href="http://get.adobe.com/flashplayer/" title="Visit adobe.com">download this for free</a> from the Adobe website. If you&#8217;re using a feed reader you could <a href="http://www.russback.com/adobe-flex/creating-a-strongly-typed-arraycollection.html">try loading this post in your web browser</a>.</p>
</div>
<h3>The application (main.mxml)</h3>
<p>In our application we begin with some basic layout properties, a call to a creationCompleteHandler() method, and a Script block with a private variable containing an instance of the ProductArrayCollection class:</p>
<pre>&lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&gt;
&lt;mx:Application
  xmlns:mx=&quot;http://www.adobe.com/2006/mxml&quot;
  layout=&quot;horizontal&quot;
  width=&quot;553&quot;
  height=&quot;450&quot;
  creationComplete=&quot;creationCompleteHandler(event);&quot;
  &gt;

  &lt;mx:Script&gt;
    &lt;![CDATA[
      import mx.messaging.Producer;
      import valueobjects.Product;
      import extensions.ProductArrayCollection;
      import mx.events.FlexEvent;

      /**
       * A strongly typed ArrayCollection that can only hold Product objects
       */
      [Bindable]
      private var _products:ProductArrayCollection = new ProductArrayCollection();</pre>
<p>Next we have our creationCompleteHandler() function that is called when the application is created. In this example, it simply adds event listeners to each of the buttons in the example. Each event listnener triggers the clickHandler() method and it&#8217;s this method that calls each of the ProductArrayCollection methods we&#8217;re concerned with: addItem(), addProduct(), addItemAt(), addProductAt(), and getProductAt().</p>
<p>The line that calls getProductAt() also grabs the returned Product and updates the view with the name of that Product. As the getProductAt() method returns a Product instead of a generic Object class, we don&#8217;t need to cast it as a Product and can therefore reference the properties of it directly.</p>
<pre>      /**
       * Adds event listeners to each of the buttons in the example
       */
      private function creationCompleteHandler(event:FlexEvent):void
      {
        this.btnAddItemJeans.addEventListener(MouseEvent.CLICK, clickHandler);
        this.btnAddProductJeans.addEventListener(MouseEvent.CLICK, clickHandler);
        this.btnAddItemAtTeeShirt.addEventListener(MouseEvent.CLICK, clickHandler);
        this.btnAddProductAtTeeShirt.addEventListener(MouseEvent.CLICK, clickHandler);
        this.btnGetProductAt.addEventListener(MouseEvent.CLICK, clickHandler);
      }			

      /**
       * Creates two example products and handles click events on
       * each of the buttons in the example
       */
      private function clickHandler(event:MouseEvent):void
      {

        var jeans:Product = new Product(&quot;Jeans&quot;, 19.99);
        var teeShirt:Product = new Product(&quot;Tee Shirt&quot;, 54.99);

        switch (event.target.id)
        {
          case &quot;btnAddItemJeans&quot;:
            this._products.addItem(jeans);
            break;
          case &quot;btnAddProductJeans&quot;:
            this._products.addProduct(jeans);
            break;
          case &quot;btnAddItemAtTeeShirt&quot;:
            this._products.addItemAt(teeShirt, 0);
            break;
          case &quot;btnAddProductAtTeeShirt&quot;:
            this._products.addProductAt(teeShirt, 0);
            break;
          case &quot;btnGetProductAt&quot;:
            this.txtLog.text = &quot;Product at 0 = &quot; + this._products.getProductAt(0).name;
            break;
        }
      }</pre>
<p>The final part of our Script block is a simple label function that the list uses to display the properties of each Product in the list:</p>
<pre>      /**
        * Returns a string containing the Product name and price
        */
      private function labelFunction(item:Object):String
      {
        var text:String = &quot;&quot;;
        if (item is Product)
        {
          var product:Product = item as Product;
          text = product.name + &quot;, £&quot; + product.price;
        }
        return text;
      }

    ]]&gt;
  &lt;/mx:Script&gt;</pre>
<p>Our MXML view components are simple: A Vbox with some text and each of the buttons that will test our class, and a list to display the Products in our _products ProductArrayCollection variable.</p>
<pre>  &lt;mx:VBox width=&quot;100%&quot;&gt;

    &lt;mx:Text
      text=&quot;Add Jeans using...&quot;
      /&gt;

    &lt;mx:Button
      label=&quot;addItem()&quot;
      id=&quot;btnAddItemJeans&quot;
      /&gt;

    &lt;mx:Button
      label=&quot;addProduct()&quot;
      id=&quot;btnAddProductJeans&quot;
      /&gt;

    &lt;mx:Text
      text=&quot;Add Tee Shirt using...&quot;
      /&gt;

    &lt;mx:Button
      label=&quot;addItemAt(0)&quot;
      id=&quot;btnAddItemAtTeeShirt&quot;
      /&gt;

    &lt;mx:Button
      label=&quot;addProductAt(0)&quot;
      id=&quot;btnAddProductAtTeeShirt&quot;
      /&gt;

    &lt;mx:VBox
      visible=&quot;{this._products.length &gt; 0}&quot;
      includeInLayout=&quot;{this._products.length &gt; 0}&quot;
      &gt;

    &lt;mx:Text
      text=&quot;Get first product using...&quot;
      /&gt;

    &lt;mx:Button
      label=&quot;getProductAt(0)&quot;
      id=&quot;btnGetProductAt&quot;
      /&gt;

    &lt;mx:Text
      id=&quot;txtLog&quot;
      /&gt;

    &lt;/mx:VBox&gt;

  &lt;/mx:VBox&gt;

  &lt;mx:List
    dataProvider=&quot;{this._products}&quot;
    width=&quot;100%&quot;
    height=&quot;100%&quot;
    labelFunction=&quot;this.labelFunction&quot;
    /&gt;

&lt;/mx:Application&gt;
</pre>
]]></content:encoded>
			<wfw:commentRss>http://www.russback.com/adobe-flex/creating-a-strongly-typed-arraycollection.html/feed</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
	</channel>
</rss>
