Correcting Scope Errors with Delegate


There are many classes in ActionScript that use the callback event handler method style. For example, MovieClip uses callback methods such as onPress() and onRelease() to handle mouse events, and LoadVars uses onLoad() and onData() to get notifications when the data has loaded. When you use an instance of such a class as a property of a custom class, it is quite likely that you'll run into scope issues. Let's look at an example.

Consider the following class that defines a LoadVars object as a property. It adds an onData() method to the LoadVars instance so that it can get a notification when the data loads. When the data loads, it attempts to assign the text to a private property of the class.

   class Example {      private var _textLoader:LoadVars;      private var _text:String;      public function get text():String {        return _text;      }      public function Example() {        _textLoader = new LoadVars();        _textLoader.onData = function(data:String):Void {          _text = data;        };        _textLoader.load("file.txt");      }    }


Although the preceding code might seem logical, it does not work as intended. When the data loads, Macromedia Flash will call the onData() method of the LoadVars property. However, within the onData() method it attempts to reference _text, which is declared as a property of the Example class. Because the onData() method is a method of _textLoader, it is scoped to _textLoader, not to the instance of the Example class. That means that when onData() gets called, it doesn't know what _text is, so it won't correctly assign the data to the private property of the Example instance.

The most obvious solution is to define the onData() method as a method of the Example class. The following code illustrates how that might look:

   class Example {      private var _textLoader:LoadVars;      private var _text:String;      public function get text():String {        return _text;      }      public function Example() {        _textLoader = new LoadVars();        _textLoader.onData = onText;        _textLoader.load("file.txt");      }      private function onText(data:String):Void {        _text = data;      }    }


Although the preceding rewrite appears to be different from the first example, it works in exactly the same way. ActionScript 2.0 is a prototype-based language, so when a function reference is assigned to a callback for an object, the function is always called as a method of the object to which the reference was assigned. Even though onText() is defined as a method of Example, when it gets called as a callback of _textLoader it is called as a method of that object, and the same scope issue exists as previously.

The solution is to use a class called mx.utils.Delegate. The Delegate class defines a static method, create(), which returns a function that will call a specified function with the correct scope. The create() method requires two parameters: the object to which you want the function call scoped and the function. The following code rewrites the Example class with the Delegate.create() method so that when onText() is called, it is called as a method of the Example instance:

   import mx.utils.Delegate;    class Example {      private var _textLoader:LoadVars;      private var _text:String;      public function get text():String {        return _text;      }      public function Example() {        _textLoader = new LoadVars();        _textLoader.onData = Delegate.create(this, onText);        _textLoader.load("file.txt");      }      private function onText(data:String):Void {        _text = data;      }    }


You can use the Delegate.create() method to correct scope issues with any callback method. For example, you can assign the return value from Delegate.create() to the onPress() method of a movie clip. You can even use Delegate.create() when registering listeners to component instances via the addEventListener() method.

In this task you'll build a text loader component that uses Delegate.create().

1.

Open textLoader1.fla from the Lesson11/Start directory.

The Flash document has a movie clip symbol called TextLoader, which has a nested TextArea instance with an instance name of _textArea. There's an instance of TextLoader on the stage. Initially, the TextLoader instance merely displays the TextArea instance. However, after you complete the following steps, it will load text from a text file and display it in the TextArea.

2.

Open a new ActionScript file and add the following class declaration:

   class TextLoader extends MovieClip {    }


Because the TextLoader class is to be associated with a movie clip symbol, it needs to subclass MovieClip.

3.

Add two import statements to the top of the code to import the mx.controls.TextArea and mx.utils.Delegate classes.

   import mx.controls.TextArea;    import mx.utils.Delegate;


The class needs to reference the nested TextArea instance, so it needs to import the TextArea class. And it needs to import Delegate so that it can call the create() method.

4.

Define the following private properties for the class.

   private var _textArea:TextArea;    private var _textUrl:String;    private var _loadVars:LoadVars;


The _textArea property references the nested TextArea. The _textUrl property is to store the URL to the text file. The _loadVars property is to reference the LoadVars object that loads the text.

5.

Define an inspectable setter method called textUrl that sets the value of _textUrl.

   [Inspectable]    public function set textUrl(url:String):Void {      _textUrl = url;    }


The textUrl setter method simply assigns a value to the _textUrl property.

6.

Define the constructor so that when the object is constructed it instantiates a new LoadVars object, adds an onData() callback, and loads the data from the URL specified by _textUrl.

   public function TextLoader() {      _loadVars = new LoadVars();      _loadVars.onData = Delegate.create(this, onText);      _loadVars.load(_textUrl);    }


The constructor loads the text from the URL specified by _textUrl, and it tells Flash to call onText() when the data loads.

7.

Define the onText() method such that when the data is loaded it is assigned to the TextArea.

   private function onText(data:String):Void {      _textArea.text = data;    }


When the data loads and the onText() method is called, assign the loaded text to the TextArea.

8.

Save the ActionScript file as TextLoader.as in the same directory as the Flash document.

The class needs to be in a directory that's part of the classpath so that Flash can locate it.

9.

Open the library in the Flash document and open the Linkage Properties dialog box for the TextLoader symbol. Select the Export For ActionScript option, and assign TextLoader to the AS 2.0 Class field. Then click OK.

The Linkage Properties dialog box lets you associate an ActionScript class with a symbol. In this case, you're associating the TextLoader class with the TextLoader symbol. So when an instance is placed on stage, the TextLoader constructor is automatically called.

10.

Open the Component Definition dialog box for the TextLoader symbol. Set the AS 2.0 Class field to TextLoader, and click OK.

The Component Definition dialog box lets you tell Flash what ActionScript class to use for the inspectable properties. In this case the TextLoader class defines one inspectable property: textUrl.

11.

If necessary, copy lorem.txt from the Lesson11/Assets directory to the directory in which you saved the Flash document. Then select the TextLoader instance on the stage, and from the Component inspector panel, set the textUrl parameter to lorem.txt.

The lorex.txt document contains text that you can use to test the class. When you set the textUrl parameter to lorem.txt, it will attempt to load the text from that file when you test the movie.

12.

Test the movie.

You'll see the text loaded into the TextArea instance.




Macromedia Flash 8 ActionScript Training from the Source
Macromedia Flash 8 ActionScript: Training from the Source
ISBN: 0321336194
EAN: 2147483647
Year: 2007
Pages: 221

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