Saturday, February 9, 2008

The Flex Tutorial: Part II - Language Concepts and Basics

The two languages of Flex

The Flex language consists of two flavors: ActionScript 3 and XML. ActionScript is a scripting language based on ECMAScript (JavaScript), used primarily for the development of websites and software using the Adobe Flash Player platform (http://en.wikipedia.org/wiki/ActionScript). The Extensible Markup Language (XML) is a general-purpose markup language, which is classified as an extensible language because it allows its users to define their own elements (http://en.wikipedia.org/wiki/XML). In Flex ActionScript files have the extension ".as" while the XML files have the extension ".mxml."

XML is used in Flex to represent static objects such as a button on a graphical user interface or an HTTP Service. For graphical user interface this provides a fast and efficient way of laying out components. For example consider an application that consists or a single panel and a single button:

Using MXML this would require the following code:

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="vertical">
 <mx:Panel width="250" height="200" layout="vertical" horizontalAlign="center" 
 verticalAlign="middle" title="This is a panel">
  <mx:Button label="This is a button"/>
 </mx:Panel>
</mx:Application>

In this Flex (MXML) code you are essentially creating an Application with a vertical layout that contains a Panel with some size, layout, and text attributes. The panel then contains a Button with some text. This is easy to conceptualize since the in the code the application contains the panel, which then contains the button.

Using Java this same equivalent application would require the following code:

import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JButton;
import javax.swing.BorderFactory;
import java.awt.Color;

public class Application extends JFrame {
    
    public static void main(String[] args) {
        JFrame frame = new Application();
        frame.setSize(400,400);
        frame.setVisible(true);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    }
    
    public Application() {
        JPanel panel = new JPanel();
        panel.setBorder(BorderFactory.createTitledBorder(
            BorderFactory.createLineBorder(Color.black), 
            "This is a panel"));
        JButton button = new JButton("This is a button");
        panel.add(button);
        setContentPane(panel);
    }
}

In the Java code the concept remains the same, that an Application contains a Panel that contains a Button, but the code is not as easy to read (even when ignoring the extra stuff that was thrown in there to close the frame and set the border).

This just serves an example of how much less coding and complexity there is with constructing graphical user interface in MXML than with other object oriented languages. Less complexity means that the application will be easier to maintain, which is extremely important considering that the vast majority of time in the software lifecycle is spent in maintenance. Complexity is not only reduced with Flex and the layout out of graphical user interface components, Flex has numerous other features that do the same across the entire framework.

Though Flex is less complex that doesn't mean that it loses any capability. The genius of Flex is its ability to represent the same conceptual models as in other object oriented languages, only with less code and complexity. Consider the class diagrams of the example Flex and Java applications:

The same relationships are maintained: an application has a panel that has a button. Flex can be used to represent almost any object oriented design exactly. I use the term "almost" because ActionScript 3 currently doesn't support abstract classes.

Flex also has the ability to represent objects using ActionScript, which can be used in place of or in conjunction with MXML. For example you could create a "Person" ActionScript class and use it to represent data in an MXML component. The actual instantiation and value assignments of the "Person" class could then be done in either MXML or ActionScript.

The following is an ActionScript representation of a "Person" class:

package example.data
{
 public class Person
 {
  public var firstName:String;
  public var lastName:String;
  public var favoriteFood:String;
 }
}

Inside XML this class could be instantiated (assuming the class was included as "ex") like so:

<ex:Person id="person" firstName="John" lastName="Valentino" favoriteFood="Steak"/>

Inside of ActionScript this class could be instantiated like the following:

var person:Person = new Person();
person.firstName = "John";
person.lastName = "Valentino";
person.favoriteFood = "Steak";

 

What the heck are you doing? Where are the getters and setters?

Getters and setters are something that I feel have gotten a bit out of hand in object-oriented programming. The purpose of using getters and setters is to decouple objects and to provide hidden functionality when assigning values to attributes of objects. For example if the "Person" class contained an instance of "Person" called "child" you probably wouldn't want to make it (and all of its attributes) publicly accessible to prevent coupling. An example of hidden functionality would be if you wanted to check modify special characters in the first and last names when setting their values.

No matter how you or I feel about getters and setters and their under usage or over usage, Flex provides its own special functions for getters and setters. This allows classes to define methods to handle the getting and setting of attributes within classes, but those functions can be called (by lazy programmers like myself) as if they were publicly accessible attributes.

Consider the "Person" class redone to use the special get and set functions:

package example.data
{
 public class PersonII
 {
  private var _firstName:String, _lastName:String, 
        _favoriteFood:String;
  
  public function get firstName():String
  {
   return _firstName;
  }
  
  public function get lastName():String
  {
   return _lastName;
  }
  
  public function get favoriteFood():String
  {
   return _favoriteFood;
  }
  
  public function set firstName(value:String):void
  {
   _firstName = value;
  }
  
  public function set lastName(value:String):void
  {
   _lastName = value;
  }
  
  public function set favoriteFood(value:String):void
  {
   _favoriteFood = value;
  } 
 }
}

These special get and set functions are what allow these functions to be called as if they were attributes. Using this new person classes the example instantiations in MXML and ActionScript to not need to be changed; they work the same as before.

Inside XML this class could be instantiated (assuming the class was included as "ex") like so:

<ex:PersonII id="person" firstName="John" lastName="Valentino" favoriteFood="Steak"/>

Inside of ActionScript this class could be instantiated like the following:

var person:PersonII = new PersonII();
person.firstName = "John";
person.lastName = "Valentino";
person.favoriteFood = "Steak";

 

Bringing MXML and ActionScript together

One of the most important concepts in Flex is that every object has 2 aspects: the XML and the ActionScript. In a constant effort to eliminate complexity I have found that XML is preferable to ActionScript in most situations. A good rule of thumb to know when to use what is to ask yourself the question "Can I do this in MXML?" If the answer is no than you can go with ActionScript, otherwise stick with MXML to cut down on code size and complexity. You must exercise common sense though; if you find yourself having to do something that seems unintuitive in MXML than you should probably use ActionScript.

Typically in graphical user interface components ActionScript is used within MXML components. This allows graphical user interfaces to be statically laid out in MXML, and then use ActionScript to handle complex logic. For example consider the original example application that has been modified to use ActionScript to display a message when a button is pressed:

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="vertical">

 <mx:Script>
  <![CDATA[
  import mx.controls.Alert;

  private function buttonPressed():void
  {
   Alert.show("The button was pressed");
  }
  ]]>
 </mx:Script>

 <mx:Panel width="250" height="200" layout="vertical" horizontalAlign="center" 
  verticalAlign="middle" title="This is a panel">
  <mx:Button label="This is a button" click="buttonPressed()"/>
 </mx:Panel>
</mx:Application>

Now when the button is pressed the following message is displayed:

 

Binding

One of the most important features of Flex is the ability to bind objects to components. This is basically instant model-view-controller without having to use listeners or worry about observers and observables. A common need in programming graphical user interfaces is the ability to display some type of information to the user through some sort of component, that is then updated through some event. For example consider a series of text fields that display information about a person:

The following code shows how the data is bound to different fields within the graphical user interface:

<?xml version="1.0" encoding="utf-8"?>
<mx:Panel xmlns:mx="http://www.adobe.com/2006/mxml" layout="vertical" 
 title="Person Panel" horizontalAlign="center">

 <mx:Script>
  <![CDATA[

  [Bindable]
  private var firstName:String = new String();
  [Bindable]
  private var lastName:String = new String();
  [Bindable]
  private var favoriteFood:String = new String();

  private function john():void
  {
   firstName = "John";
   lastName = "Valentino II";
   favoriteFood = "Steak";
  }

  private function sarah():void
  {
   firstName = "Sarah";
   lastName = "Valentino";
   favoriteFood = "Pizza";
  }

  ]]>
 </mx:Script>
 <mx:Form width="100%">
  <mx:FormItem label="First Name:">
   <mx:TextInput text="{firstName}"/>
  </mx:FormItem>
  <mx:FormItem label="Last Name:">
   <mx:TextInput text="{lastName}"/>
  </mx:FormItem>
  <mx:FormItem label="Favorite Food:">
   <mx:TextInput text="{favoriteFood}"/>
  </mx:FormItem>
 </mx:Form>
 <mx:HBox>
  <mx:Button label="John" click="john()"/>
  <mx:Button label="Sarah" click="sarah()"/>
 </mx:HBox>

</mx:Panel>

The variables firstName, lastName, and favoriteFood, are made "Bindable" so that when ever they are changed anything that is referencing them is also changed. This means that since those variables are referenced by the TextInputs, when those variables are changed what is displayed in those TextInputs will also change.

For example the following is displayed when the "John" button is pressed:

The following is displayed when the "Sarah" button is pressed:

It should also be noted that if you declare instances in MXML that are automatically bindable, so the following example retains the same functionality:

<?xml version="1.0" encoding="utf-8"?>
<mx:Panel xmlns:mx="http://www.adobe.com/2006/mxml" layout="vertical" 
 title="Person Panel" horizontalAlign="center">
 
 <mx:String id="firstName"/>
 <mx:String id="lastName"/>
 <mx:String id="favoriteFood"/>

 <mx:Script>
  <![CDATA[

  private function john():void
  {
   firstName = "John";
   lastName = "Valentino II";
   favoriteFood = "Steak";
  }

  private function sarah():void
  {
   firstName = "Sarah";
   lastName = "Valentino";
   favoriteFood = "Pizza";
  }

  ]]>
 </mx:Script>
 <mx:Form width="100%">
  <mx:FormItem label="First Name:">
   <mx:TextInput text="{firstName}"/>
  </mx:FormItem>
  <mx:FormItem label="Last Name:">
   <mx:TextInput text="{lastName}"/>
  </mx:FormItem>
  <mx:FormItem label="Favorite Food:">
   <mx:TextInput text="{favoriteFood}"/>
  </mx:FormItem>
 </mx:Form>
 <mx:HBox>
  <mx:Button label="John" click="john()"/>
  <mx:Button label="Sarah" click="sarah()"/>
 </mx:HBox>

</mx:Panel>

If you want to use binding with a custom class however, such as with the Person class, you have to declare the class itself or its individual attributes as bindable. This allowing the class to be bound, but the instance still has to be declared as bindable. For example consider the bindable Person class:

package example.data
{
 [Bindable]
 public class Person
 {
  public var firstName:String;
  public var lastName:String;
  public var favoriteFood:String;
 }
}

To use the bindable Person class in the application example the Person instance has to be marked as "Bindable." The following is the application redone to use the Person class:

<?xml version="1.0" encoding="utf-8"?>
<mx:Panel xmlns:mx="http://www.adobe.com/2006/mxml" layout="vertical" 
 title="Person Panel" horizontalAlign="center">

 <mx:Script>
  <![CDATA[
  import example.data.Person;
  
  [Bindable]
  private var person:Person = new Person();

  private function john():void
  {
   person.firstName = "John";
   person.lastName = "Valentino II";
   person.favoriteFood = "Steak";
  }

  private function sarah():void
  {
   person.firstName = "Sarah";
   person.lastName = "Valentino";
   person.favoriteFood = "Pizza";
  }

  ]]>
 </mx:Script>
 <mx:Form width="100%">
  <mx:FormItem label="First Name:">
   <mx:TextInput text="{person.firstName}"/>
  </mx:FormItem>
  <mx:FormItem label="Last Name:">
   <mx:TextInput text="{person.lastName}"/>
  </mx:FormItem>
  <mx:FormItem label="Favorite Food:">
   <mx:TextInput text="{person.favoriteFood}"/>
  </mx:FormItem>
 </mx:Form>
 <mx:HBox>
  <mx:Button label="John" click="john()"/>
  <mx:Button label="Sarah" click="sarah()"/>
 </mx:HBox>

</mx:Panel>