Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

...

The FlexJS framework is designed to work well with ActionScript and JavaScript. The philosophy is that if it works in ActionScript, it should work in JavaScript, too, and vice-versa. You create a component (or bead) in one language and then port that functionality to the other language. Note that you port the functionality and not the codeActionScript, designating platform-specific functionality or elements, using the COMPILE::SWF and COMPILE::JS compiler directives. See FlexJS Component Source Code Patterns for details about this development pattern for FlexJS.

You want to make things as efficient as possible in both environments. For instance, the TextInput component in ActionScript is comprised of view and model beads. The JavaScript TextInput has neither since HTML has a view and model already (via the HTMLElement); the FlexJS TextInput component is just a thin wrapper for the HTML control. When As you begin creating your JavaScript component, take the time to understand the best way to represent it so the component is as optimal as possible .

As components become more complex, such as with the Slider component, more of the ActionScript code is present in the JavaScript component.

Creating a Bead

The first example shows how to make the password bead. You will see how to compose the bead and how to add it to an existing component's strand. Since FlexJS is available in ActionScript and JavaScript, the example shows how to make the bead using both languages simultaneously.

Panel

This example already exists in the FlexJS library. You can look at the class files and follow along. (See; flex-js/frameworks/projects/HTML/as/src/org/apache/flex/html/accessories/PasswordInputBead.as)

For this example, the password bead will modify the underlying text control (a TextField for ActionScript and the <input> element for JavaScript) so that it becomes a password entry field. These are the steps needed for ActionScript:

  1. Create the bead class (PasswordInputBead).
  2. Implement the strand setter, which is required. The strand is the component (in this case, TextInput) which may or may not have all of is beads set when the strand setter is called.
  3. In the strand setter, a listener is set up for the “viewChanged” event which is sent when the strand’s view bead is added. The view bead has access to the underlying text field.
  4. When the “viewChanged” event is handled, the viewBead is used to get to the text field and the text field is modified to display its entry as a password.

These are the JavaScript steps:

  1. Create the bead class (PasswordInputBead).
  2. Implement the strand setter, which is required.
  3. In the strand setter, set the HTML element's type to 'password'.

Create the Bead

Create a new ActionScript class called PasswordInputBead and have it implement the IBead interface. Beads do not normally have their own user interface or display; they normally use the strand. Beads can create new UI components, but then they make the strand the parent of those components.

Code Block
// actionscript
package org.apache.flex.html.accessories
{
...
    public class PasswordInputBead implements IBead

Do the same for JavaScript and create the PasswordInputBead class - JavaScript versions must have the same class path as their ActionScript counterparts.

Code Block
// javascript
goog.provide('org.apache.flex.html.accessories.PasswordInputBead');
...
org.apache.flex.html.accessories.PasswordInputBead = function()

Implement the Strand Setter

Declare a private var called “_strand” and implement the setter function:

Code Block
// actionscript
public function set strand(value:IStrand):void
{
    _strand = value;
}
Code Block
// javascript
PasswordInputBead.prototype.set_strand = function(value)
{
    this.strand_ = value;
}

Set Up a Listener for the “viewChanged” Event

Add the following line to the strand setter function:

Code Block
//actionscript
IEventDispatcher(value).addEventListener(“viewChanged”,viewChangeChandler);

There is no JavaScript equivalent for this step since the view will already be set up. Most of your work to set up the bead will be in the strand setter function.

Implement the Bead

For ActionScript, create the function to handle the “viewChanged” event:

Code Block
// actionscript
private function viewChangedHandler(event:Event):void
{
    var textView:ITextFieldView = _strand.getBeadByType(ITextFieldView) as ITextFieldView;
    if (textView) { 
        var textField:CSSTextField = textView.textField; 
        textField.displayAsPassword = true; 
    }
}

The first thing the event handler does is retrieve the ITextFieldView bead (an interface, not an actual class) from the strand using the getBeadByType() function. This function will examine the strand and return the first bead that matches the given type.

With an ITextFieldView bead in hand, the next thing to do is get its underlying Flash textField and set it to display as a password. CSSTextField is a FlexJS class that extends the ActionScript TextField to make it easier to apply styles to it using CSS.

For Javascript, add the following line to the set_strand function:

Code Block
// javascript
value.element.type = 'password';

so the set_strand function now reads:

Code Block
// javascript
PasswordInputBead.prototype.set_strand = function(value)
{
    this.strand_ = value;
    value.element.type = 'password';
}

FlexJS JavaScript components have a property called element which is the base HTML element for the component. This is often a <div> when the component is complex, but for lower-level component, such as TextInput, the element property is usually the corresponding HTML element which is <input> in this case.

for both SWF and JS execution.

Bead Life Cycle

There are three ways a bead can be used with a component:

  1. Place an instance of the bead in-line in an MXML file using the component's <js:beads> property.
  2. Reference a bead's class in a CSS style for the component.
  3. Create the bead in ActionScript code (using new operator) and place it onto the component's strand using the addBead() function.

When a component is added to the display list via the addElement() function of its parent, the FlexJS framework invokes a function on the component called, addedToParent().

  1. The addedToParent() function will look at the <js:beads> list and add those beads to the strand. In doing so, each bead's strand setter (see below) is called. 
  2. Once the <js:beads> are handled, addedToParent() then sees if there are model, view, and controller beads that need to be added to the strand. Using the model bead as an example, addedToParent() checks to see if the model is already on the strand (it may have been in the <js:beads> list). If not, then the style for the component is checked to see if iBeadModel was specified and if it is, a new model is created and added to the strand. This sequence applies to the view and controller beads as well.
  3. Finally, addedToParent() dispatches the "beadsAdded" event on the component.

By first processing the <js:beads>, the FlexJS framework allows a developer to replace default beads set up in CSS. If you write your own components and have custom beads, follow this pattern:

  1. Override addedToParent() and call super.addedToParent() immediately. This will perform the three steps above. It will also dispatch "beadsAdded".
  2. Check to see if your custom bead is already on the strand. 
  3. If the bead is not on the strand, create a default bead either by looking in CSS to see if a class has been specified or just create one using the new operator. Then add it to the strand.
  4. Dispatch a custom event or dispatch "initComplete" (if your component is not subclassing a container class which will dispatch "initComplete" for you) to signal that all beads are now present.

Some tips are where to perform certain tasks are given in the topics below.

Creating a Bead

The first example shows how to make the password bead. You will see how to compose the bead and how to add it to an existing component's strand.

Panel

This example already exists in the FlexJS library. You can look at the class files and follow along. (See; flex-js/frameworks/projects/HTML/as/src/org/apache/flex/html/accessories/PasswordInputBead.as)

For this example, the password bead will modify the underlying text control (a TextField for ActionScript and the <input> element for JavaScript) so that it becomes a password entry field. These are the steps needed:

  1. Create the bead class (PasswordInputBead).
  2. Implement the strand setter, which is required. The strand is the component (in this case, TextInput) which may or may not have all of is beads set when the strand setter is called.
  3. In the strand setter, a listener is set up for the “viewChanged” event which is sent when the strand’s view bead is added. The view bead has access to the underlying text field.
  4. When the “viewChanged” event is handled, the viewBead is used to get to the text field and the text field is modified to display its entry as a password.

Create the Bead

Create a new ActionScript class called PasswordInputBead and have it implement the IBead interface. Beads do not normally have their own user interface or display; they normally use the strand. Beads can create new UI components, but then they make the strand the parent of those components.

Code Block
package org.apache.flex.html.accessories
{
...
    public class PasswordInputBead implements IBead

 

Implement the Strand Setter

Declare a private var called “_strand” and implement the setter function:

Code Block
public function set strand(value:IStrand):void
{
    _strand = value;
    // more, see next paragraph
}

The implementation of the bead differs between JavaScript and SWF. In the browser, the underlying component is an input element and all that this bead needs to do is set its type to be "password". For the SWF platform, it is more complicated with events being intercepted and the text being changed to obscure the password. The strand setter function has these additional lines to complete it.

The strand setter is a good place to initialize values and to set up event listeners, such as "viewChanged", "beadsAdded", or "initComplete" (or a custom event dispatched by your component).

Code Block
COMPILE::SWF {
    IEventDispatcher(value).addEventListener("beadsAdded",beadsAddedHandler);
}
COMPILE::JS {
    var host:UIBase = value as UIBase;
    var e:HTMLInputElement = host.element as HTMLInputElement;
	e.type = 'password'; 
}

The compile directives will have the input element set up for password input with just a few lines when they are cross-compiled into JavaScript. For ActionScript, an event listener is set up to listen for when the standard beads (model, view, controller) have been added to the strand; this code is not needed in JavaScript and so it is isolated to ActionScript using the COMPILE::SWF directive.

Implement the Event Handler

For ActionScript, create the function to handle the “beadsAddedEvent” event (note that the COMPILE::SWF directive is placed above the function definition so all of the function is included only in the ActionScript build).

Code Block
COMPILE::SWF
private function beadsAddedHandler(event:Event):void
{
    var textView:ITextFieldView = _strand.getBeadByType(ITextFieldView) as ITextFieldView;
    if (textView) { 
        var textField:CSSTextField = textView.textField; 
        textField.displayAsPassword = true; 
    }
}

The first thing the event handler does is retrieve the ITextFieldView bead (an interface, not an actual class) from the strand using the getBeadByType() function. This function will examine the strand and return the first bead that matches the given type.

With an ITextFieldView bead in hand, the next thing to do is get its underlying Flash textField and set it to display as a password. CSSTextField is a FlexJS class that extends the ActionScript TextField to make it easier to apply styles to it using CSSThis is an example of making a functionally equivalent bead in JavaScript. Since the JavaScript component is just wrapping the HTML input element, which is a property of the strand, the bead can take the direct route of setting the element's type to "password".

A More Complex Bead

The PasswordInputBead is fairly simple: it just changes the type of input control using the built-in input control's property (displayAsPassword for ActionScript and type = 'password' for JavaScript). If you want the bead to do more, you can have the bead listen to events and take its own actions. Here is the valueChangedHandler beadsAddedHandler for a bead that restricts input to be numeric and makes sure the text being entered is valid.

Code Block
// actionscript
private var _decimalSeparator:String = ".";
public function get decimalSeparator():String
{
    return _decimalSeparator;
}
public function set decimalSeparator(value:String):void
{
    if (_decimalSeparator != value) {
        _decimalSeparator = value;
    }
}
private function viewChangeHandlerbeadsAddedHandler(event:Event):void
{			
    // get the ITextFieldView bead, which is required for this bead to work
    var textView:ITextFieldView = _strand.getBeadByType(ITextFieldView) as ITextFieldView;
    if (textView) {
        COMPILE::SWF {
            var textField:CSSTextField = textView.textField;
            textField.restrict = "0-9" + decimalSeparator;

            // listen for changes to this textField and prevent non-numeric values, such
            // as 34.09.94
            textField.addEventListener(TextEvent.TEXT_INPUT, handleTextInput);
        }
}
        COMPILE::JS {
            var host:UIBase = _strand as UIBase;
            var e:HTMLInputElement = host.element as HTMLInputElement;
            e.addEventListener('keypress', validateInput);
        }
    }
}

Like the PasswordInputBead, the ActionScript platform uses a special property of the TextField to restrict the input to numerals and a decimal separator (a property on this bead). While the restrict property of the TextField does limit input to those characters, Like the PasswordInputBead, this bead's viewChangeHandler, uses a special property of the TextField to restrict the input to numerals and a decimal separator (a property on this bead). While the restrict property of the TextField does limit input to those characters, it does not prevent those characters from repeating. That is, you can enter "123.45.67.890" which, of course, is not a valid number.

To have the bead act as an input validator, the bead listens for the TEXT_INPUT event on the TextField using the handleTextInput() function:

Code Block
// actionscriptCOMPILE::SWF
private function handleTextInput(event:TextEvent):void
{
    var insert:String = event.text;
    var caretIndex:int = (event.target as CSSTextField).caretIndex;
    var current:String = (event.target as CSSTextField).text;
    var value:String = current.substring(0,caretIndex) + insert + current.substr(caretIndex);
    var n:Number = Number(value);
    if (isNaN(n)) event.preventDefault();
}

...

The JavaScript version of this bead works similarly to the JavaScript PasswordInputBead where you set things up in the strand setter and listen for the appropriate event, a keypress event in this case :

Code Block
// javascript strand setter
    value.addEventListener('keypress',goog.bind(this.validateInput, this));

as shown in the COMPILE::JS code block.

Unlike ActionScript, JavaScript input elements (prior Unlike ActionScript, JavaScript input elements (prior to HTML 5) do not have any way to restrict input characters, so you have to do that yourself, which is done in the validateInput() event handler:

Code Block
// javascriptCOMPILE::JS
private function validateInput(event:Event):void
{
  var code = event.charCode;
  
  // backspace or delete
  if (event.keyCode == 8 || event.keyCode == 46) return;
  
  // tab or return/enter
  if (event.keyCode == 9 || event.keyCode == 13) return;
  
  // left or right cursor arrow
  if (event.keyCode == 37 || event.keyCode == 39) return;
  
  var key = String.fromCharCode( code );

  var regex = /[0-9]|\./;
  if( !regex.test(key) ) {
    event.returnValue = false;
    if(event.preventDefault) event.preventDefault();
    return;
  }
  var cursorStart = event.target.selectionStart;
  var cursorEnd   = event.target.selectionEnd;
  var left = event.target.value.substring(0,cursorStart);
  var right = event.target.value.substr(cursorEnd);
  var complete = left + key + right;
  if (isNaN(complete)) {
    event.returnValue = false;
    if(event.preventDefault) event.preventDefault();
  }

...

To make use of the bead, go to your application or initial view MXML file and create a TextInput and add the PasswordInputBead to it:

Code Block
<basic<js:TextInput>
	<basic<js:beads>
		<basic<js:PasswordInputBead />
	</basicjs:beads>
</basicjs:TextInput>

The FlexJS framework first adds any beads that are declared in MXML. After that, FlexJS adds beads for the component that are declared in a style sheet. FlexJS uses defaults.css as its style sheet where TextInputView is declared as the view bead for TextInput (more on this later). When the TextInputView bead is added, it triggers the “viewChanged” event which allows the PasswordInputBead to make its modifications.

To see how powerful adding beads can be, add another bead to this strand:

Code Block
<basic<js:TextInput>
	<basic<js:beads>
		<basic<js:PasswordInputBead />
		<based:TextPromptBead prompt=”password” />
	</basicjs:beads>
</basicjs:TextInput>

When the application is run, not only can you enter text as a password, a prompt will appear as long as the field is empty.

Of course, if you find that you frequently need a set of components, creating a composite, custom component that combines the beads needed is also possible; add the beads in your custom component's addedToParent() override function.

Bead Guidelines

  • Most beads use their strand for their UI parent. Beads can make or use other components or visual parts, then they add them to their strand’s display list.
  • Try to use interfaces whenever possible. This keeps implementation code separate and lets modules work better.
  • Use the paradigm of separation of concerns as much as possible. That is, do not have beads do more than necessary and separate functions into multiple beads. Very often a component will have one bead for the visual display (a view bead), one bead to hold its data (a model bead), and one bead to handle user interactions (a control bead). This allows a developer to swap out beads as necessary. For example, a mobile developer might want to replace the Slider's mouse controller bead with a touch controller bead: the component's other parts (track, thumb, model, etc.) remain the same.

Making a Component

...

In the previous example, a new bead was created and added to an existing bead. These steps show you how to make a new component. Remember that components are usually made up of a model bead, a view bead, and a control bead, but this depends on what the component does.

...

If you open Slider.as, you'll see that it implements the setters and getters, but all they do is call upon the model's corresponding setters and getters. Because Slider extends UIBase, the display list is provided as well as common functionality that all FlexJS components need (such as setting size and position). Your strand really needs to provide the public interface and that's about it. The strand should not create any views or manipulate any values. There are probably going to be exceptions, but this should be the rule: keep functionality as separate as possible and join it together with events.

SliderView (view bead)

If you open the SliderView.as file and look at the strand setter function, you can see how the track and thumb beads are identified and added.

Code Block
_track = new Button();
Button(_track).addBead(new (ValuesManager.valuesImpl.getValue(_strand, "iTrackView")) as IBead);

_thumb = new Button();
Button(_thumb).addBead(new (ValuesManager.valuesImpl.getValue(_strand, "iThumbView")) as IBead);

Both the track and thumb parts of the Slider are Buttons. Since Buttons are also FlexJS components and follow the same pattern, they too have beads. The look of the track and slider are encapsulated in Button-compatible beads so they are fetched from the style definition by the ValuesManager and added to the Button's strand.

Once the pieces of the view are created, event listeners are set up so the view knows what's happening to the strand, especially the size and the value. Changes to the size cause the view to layout out its children. Changes to the value position the thumb over the track.

RangeModel (model bead)

If you open the RangeModel.as (or any of the other model files) you'll see they are a collection of property setters and getters (and their backing variables, of course). Every property has an event dispatched when it changes. Note that the event is dispatched from the model, not its strand. Beads that need to listen for changes to the model should fetch the strand's model and set up listeners for those properties it is interested in. For example, the Slider's mouse controller bead can update the model value which will be picked up the Slider's view bead and the thumb will change position.

This is the typical pattern for getting the strand's model and setting up the listener. Note the use of interfaces instead of concrete classes.

Code Block
// actionscript
var model:IBeadModel = _strand.getBeadByType(IBeadModel) as IBeadModel;
IEventDispatcher(model).addEventListener("valueChanged",modelListener);

SliderMouseController (control bead)

If you open the SliderMouseController.as file you will see that it uses the strand setter to get the model and view. The controller's job is to coordinate the actions of the user with the model and view. A controller sets up the input event handlers (e.g., mouse events, keyboard events, touch events) to do that. The SliderMouseController sets up mouse events such that when the track is clicked, the controller catches that event, derives the value using the model and the x position of the click. The controller then updates the model with a new value. Because the model dispatches an event when the value changes, the view, listening for this event, changes the position of the thumb.

When you write your own controllers you can follow the same pattern: translate events to model values and update the model. The view bead(s) should be listening for those changes.

Making a Component - JavaScript

This example uses the same component, Slider, but looks at how to do this in JavaScript.

Some FlexJS JavaScript components may have less parts then their ActionScript counterparts, but this is largely due to the fact that the environment, HTML, has a lot to offer and FlexJS components strive to be as efficient as possible. The JavaScript Slider component does mimic the ActionScript component pretty closely, mainly because there is no HTML slider.

The FlexJS JavaScript Slider component has the following parts:

Slider.js: This is the strand piece and creates the base HTML element (a <div>) for the other parts to use as their parent element. This is similar to the ActionScript Slider strand which is the display list for its parts. Both the JavaScript and ActionScript Slider classes extend UIBase. The big difference is that the ActionScript UIBase creates the display list while the JavaScript UIBase only provides the core functions of a strand, leaving the base element up to the component strand. Like its ActionScript counterpart, the JavaScript slider also provides the setters and getters for the properties that are stored in its model.

Panel

At this time there is no JavaScript complement to ValuesManager. In other words, beads are set or selected for a component via CSS; this is still on the task list for FlexJS. Beads are explicitly named and applied in the main strand code.

RangeModel.js: This is a bead that provides the data model, including the minimum, maximum, and current values that the Slider is representing. Changes to the model cause events to be dispatched just as they are in the ActionScript version.

SliderTrackView.js: This is a bead that provides the track piece.

SliderThumbView.js: This is a bead that provides the thumb piece.

SliderMouseController.js: Just like its ActionScript counterpart, the JavaScript SliderMouseController detects the mouse clicks and drags as events and updates the model. Any component you write that accepts keyboard and/or mouse and/or touch input should have a controller to handle those events.

Slider (strand)

If you open Slider.js and look at the set_element() function, you'll see that it creates its base element (a <div>) and then creates the track and thumb beads (corresponding SliderTrackView and SliderThumbView JavaScript classes).

Code Block
// javascript
  this.element = document.createElement('div');
  this.element.style.width = '200px';
  this.element.style.height = '30px';

  this.track = new org.apache.flex.html.staticControls.beads.SliderTrackView();
  this.addBead(this.track);

  this.thumb = new org.apache.flex.html.staticControls.beads.SliderThumbView();
  this.addBead(this.thumb);

All FlexJS JavaScript components have an element property that refers to the HTML element that is the parent of any other elements that make up the component. Most of the time this will be a <div> element, but it can also be an <input>, <button>, or whatever you feel is best for your component.

The JavaScript component is also has its size coded; once a the JavaScript version of ValuesManager has been implemented, this can be removed and components can have their sizes set using styles.

For the Slider, the track and thumb are actual beads (SliderTrackView and SliderThumbView, respectively). Compare this to Spinner (see Spinner.js) which uses two FlexJS TextButton components for the increment and decrement buttons.

Once the track and thumb beads are created, the mouse controller bead is created and added to the strand.

Code Block
 this.controller = new org.apache.flex.html.staticControls.beads.controllers.
                    SliderMouseController();
  this.addBead(this.controller);

To complete a FlexJS JavaScript component, make sure to set or create the positioned member and the flexjs_wrapper. FlexJS relies on the positioner to be set and uses it to size and position the component.

Code Block
  this.positioner = this.element;
  this.element.flexjs_wrapper = this;

SliderTrackView and SliderThumbView (view beads)

If you open SliderThumbView.js or SliderTrack.js, you'll see that these beads use their set_strand() functions in a way that's similar to Slider.js: an element is created, options or styles set, the element and is added to the HTML DOM.

SliderMouseController (controller bead)

If you open SliderMouseController.js, you see that its set_strand() function gets the track and thumb beads from its strand, via getBeadByType() and adds event listeners to them.

Code Block
this.track = this.strand_.getBeadByType(
        org.apache.flex.html.staticControls.beads.SliderTrackView);
this.thumb = this.strand_.getBeadByType(
        org.apache.flex.html.staticControls.beads.SliderThumbView);

goog.events.listen(this.track.element, goog.events.EventType.CLICK,
                     this.handleTrackClick, false, this);

goog.events.listen(this.thumb.element, goog.events.EventType.MOUSEDOWN,
                     this.handleThumbDown, false, this);

The purpose of the controller is to coordinate the input from the mouse with values from the model which are then reflected in the views. Here is the handleTrackClick function:

Code Block
var xloc = event.clientX;
var p = Math.min(1, xloc / parseInt(this.track.element.style.width, 10));
var n = p * (this.strand_.get_maximum() - this.strand_.get_minimum()) +
          this.strand_.get_minimum();
 
this.strand_.set_value(n);
 
this.origin = parseInt(this.thumb.element.style.left, 10);
this.position = parseInt(this.thumb.element.style.left, 10);
 
this.calcValFromMousePosition(event, true);
 
this.strand_.dispatchEvent(new org.apache.flex.events.Event('valueChanged'));

JavaScript Component Tips

Building the JavaScript component follows the pattern of the ActionScript component: base or strand piece then beads to provide the view, model, and control. Not all FlexJS JavaScript components will have these parts - it really depends on the component and how much the underlying HTML elements do (and how they work); you might not achieve a complete 1:1 (ActionScript:JavaScript) balance.

Set the positioner property with the element of your component you want used to size and place your component. Functions from UIBase, such as set_x() and set_width(), modify the corresponding styles on the component's positioner.

At this point, FlexJS is intended to be compatible with IE8 and as such, implicit getters and setters may not work with every implementation of JavaScript. For example, doing slider.value = 4, will not trigger the Slider's set_value() function. You should use the setter (and getter) functions explicitly: slider.set_value(4).

Using JavaScript Component Libraries with FlexJS

Many web applications now use JavaScript component sets, such as jQuery, and it is possible to use those frameworks with FlexJS; it is just a matter of changing how the components are built. The FlexJS package comes with a handful of jQuery-compatible and CreateJS-compatible components to get you started.

Panel

Remember that FlexJS works in both the ActionScript and JavaScript worlds. While you do not have to make ActionScript equivalents of your JavaScript components, it is good practice to provide them just so your application can be run in both environments. You will find ActionScript versions of the sample jQuery and CreateJS FlexJS components in the corresponding ActionScript packages: org/apache/flex/jquery and org/apache/flex/createjs, respectively.

jQuery for FlexJS

The biggest trick is to pull in the component set definition files. At this time, FlexJS does not have an easy way to do this. It is a manual process at this point:

  1. Compile your Flex application using Falcon JX.
  2. Open the index.html file that was generated.
  3. Insert the following lines into the <head> portion of the file.

    Code Block
    <link rel="stylesheet" href="http://code.jquery.com/ui/1.10.2/themes/smoothness/jquery-ui.css" />
    <script src="http://code.jquery.com/jquery-1.9.1.js"></script>
    <script src="http://code.jquery.com/ui/1.10.2/jquery-ui.js"></script>
    

    In the FlexJS directories you will find a small set of components in the org/apache/flex/html/jquery folder. If you compare these to the standard components in the basic package, you will see there are few differences, the most obvious being the inclusion of jQuery function calls to modify the components.

Open the TextButton.js file and look for the createElement() function:

Code Block
org.apache.flex.jquery.staticControls.TextButton.prototype.createElement =
    function() {
  this.element = document.createElement('button');
  this.element.setAttribute('type', 'button');
  $(this.element).button();

  this.positioner = this.element;
  this.element.flexjs_wrapper = this;
};

The only difference between the jQuery version of the TextButton and the basic Text button is the jQuery button-making function, $(this.element).button(). Your jQuery components may differ greatly from their non-jQuery

Once you've created or converted the components to use jQuery, the JavaScript side of FlexJS can use jQuery functions to manipulate the components and their beads. For example, might want to create a bead that manipulates the styles and provides effects such as fades. Rather than modifying every component to have the ability to fade in and out, you can just add the "FadeBead" to a component strand.

CreateJS for FlexJS

CreateJS is an altogether different way of making components from jQuery. CreateJS is more like ActionScript in that you build things rather manipulate them. As with jQuery, the big trick is to bring in the CreateJS package:

  1. Compile your Flex application with the Falcon JX compiler.
  2. Open the JavaScript file that was generated from your main Flex application (e.g., if your Flex application's main file is Main.mxml, open Main.js).
  3. Insert the following lines before the goog.provide() statements:

    Code Block
    var mainjs = document.createElement('script');
    mainjs.src = './createjs/easeljs-0.6.0.min.js';
    document.head.appendChild(mainjs);
    
  4. Be sure to put a copy of the CreateJS component files in a sub-directory called, createjs, next to the other source directories.

If you open the TextButton.js file in the CreateJS package, you will find that the createElement() function is rather different from either the basic TextButton or jQuery version of the TextButton:

Code Block
org.apache.flex.createjs.staticControls.TextButton.prototype.createElement =
function(p) {

    this.buttonBackground = new createjs.Shape();
    this.buttonBackground.name = 'background';
    this.buttonBackground.graphics.beginFill('red').
      drawRoundRect(0, 0, 200, 60, 10);

    this.buttonLabel = new createjs.Text('button', 'bold 24px Arial',
      '#FFFFFF');
    this.buttonLabel.name = 'label';
    this.buttonLabel.textAlign = 'center';
    this.buttonLabel.textBaseline = 'middle';
    this.buttonLabel.x = 200 / 2;
    this.buttonLabel.y = 60 / 2;

    this.element = new createjs.Container();
    this.element.name = 'button';
    this.element.x = 50;
    this.element.y = 25;
    this.element.addChild(this.buttonBackground, this.buttonLabel);
    p.addChild(this.element);

    this.positioner = this.element;
    this.element.flexjs_wrapper = this;
};

In this example, CreateJS functions construct a button-like object rather than use HTML elements. While the basic, jQuery, and CreateJS TextButtons diff in implementation, they are expressed exactly the same in MXML.

Using FlexJS jQuery and CreateJS Components in MXML

Once you've created your jQuery or CreateJS (or other component library) beads and components, you can add them to your Flex application MXML using namespaces. This is the root tag for a version of MyInitialView.mxml:

Code Block
<basic:ViewBase xmlns:fx="http://ns.adobe.com/mxml/2009"
		xmlns:basic="library://ns.apache.org/flexjs/basic"
		xmlns:jquery="library://ns.apache.org/flexjs/jquery"
                xmlns:createjs="library://ns.apache.org/flexjs/createjs"
	>

Once you have declared the namespaces, you use the namespaces to identify the components:

Code Block
<!-- the basic tex button component -->
<basic:TextButton label="Basic" />

<!-- the jQuery text button component -->
<jquery:TextButton label="jQuery" />

<!-- the CreateJS text button component -->
<createjs:TextButton label="CreateJS" />

Cross-Compiling

Another intriguing option for building the JavaScript side of a component is to use cross-compilation. That is, build your component in ActionScript and let the FalconJX compiler create the JavaScript version. There is catch, however.

  1. Your component must be composition of existing components.
  2. Your component must not use any Flash libraries (unless you have a JavaScript equivalent).
  3. Your component should not need any custom work on the JavaScript side.

The last issue can be waived if all you want to do is use cross compilation once (or until you are happy enough with the JavaScript result) just to get a head start.

Take the DataGrid component as an example. This component is built from Container, ButtonBar, and List. There isn't anything Flash-specific about it nor does it need customization on the JavaScript side. All of the DataGrid beads (models, views, itemRenderer) are purely ActionScript.

Another good example is the BarChart. This component was developed in a project in its own package (org.apache.flex.charts). Everything but the bars in the chart translated to JavaScript easily. When it came time to draw the bars, a component was created in ActionScript, FilledRectangle, that used the Flash Shape class. This does not have a JavaScript counterpart, so a FilledRectangle.js class was hand-crafted in the JavaScript portion of the SDK (using the same package name, of course). This allowed the BarChart component classes to be cross-compiled to JavaScript. When run in a browser, the FilledRectangle.js used a <div> element for each bar vs. the FilledRectangle.as class that used a Shape.

The Slide extends UIBase, which on the SWF side will be DisplayObject and on the JavaScript side will be a <div> element. That is fine for Slider, but in case you want to use a different HTML element, you can override the createElement() function which is present only for the JavaScript side:

Code Block
languageactionscript3
/**
 * @flexjsignorecoercion org.apache.flex.core.WrappedHTMLElement
 */
COMPILE::JS
override protected function createElement():WrappedHTMLElement
{
    element = document.createElement('div') as WrappedHTMLElement;
    element.style.width = '200px';
    element.style.height = 30px';
    // set anything else here
    return element;
}

Notice the use of flexjsignorecoercion doc tag in the comment. This tells the cross-compiler not to generate JavaScript for the "as" construct involving WrappedHTMLElement.

SliderView (view bead)

If you open the SliderView.as file and look at the strand setter function, you can see how the track and thumb beads are identified and added.

Code Block
_track = new Button();
Button(_track).addBead(new (ValuesManager.valuesImpl.getValue(_strand, "iTrackView")) as IBead);

_thumb = new Button();
Button(_thumb).addBead(new (ValuesManager.valuesImpl.getValue(_strand, "iThumbView")) as IBead);

Both the track and thumb parts of the Slider are Buttons. Since Buttons are also FlexJS components and follow the same pattern, they too have beads. The look of the track and slider are encapsulated in Button-compatible beads so they are fetched from the style definition by the ValuesManager and added to the Button's strand.

Once the pieces of the view are created, event listeners are set up so the view knows what's happening to the strand, especially the size and the value. Changes to the size cause the view to layout out its children. Changes to the value position the thumb over the track.

Be sure to add any sub-components to the strand's display list shortly after creating them. This will fire off those sub-components' life cycles and allow them to trigger their own compositions. This way, when it comes time for your view bead to size and position the sub-elements, they will have dimension. 

RangeModel (model bead)

If you open the RangeModel.as (or any of the other model files) you'll see they are a collection of property setters and getters (and their backing variables, of course). Every property has an event dispatched when it changes. Note that the event is dispatched from the model, not its strand. Beads that need to listen for changes to the model should fetch the strand's model and set up listeners for those properties it is interested in. For example, the Slider's mouse controller bead can update the model value which will be picked up the Slider's view bead and the thumb will change position.

This is the typical pattern for getting the strand's model and setting up the listener. Note the use of interfaces instead of concrete classes.

Code Block
// actionscript
var model:IBeadModel = _strand.getBeadByType(IBeadModel) as IBeadModel;
IEventDispatcher(model).addEventListener("valueChanged",modelListener);

SliderMouseController (control bead)

If you open the SliderMouseController.as file you will see that it uses the strand setter to get the model and view. The controller's job is to coordinate the actions of the user with the model and view. A controller sets up the input event handlers (e.g., mouse events, keyboard events, touch events) to do that. The SliderMouseController sets up mouse events such that when the track is clicked, the controller catches that event, derives the value using the model and the x position of the click. The controller then updates the model with a new value. Because the model dispatches an event when the value changes, the view, listening for this event, changes the position of the thumb.

When you write your own controllers you can follow the same pattern: translate events to model values and update the model. The view bead(s) should be listening for those changes.

If you open SliderMouseController.as, you see that its strand setter function gets the track and thumb beads from its strand, via getBeadByType() and adds event listeners to them. Notice that there are differences between the ActionScript and JavaScript platforms, so these are coded accordingly.

Code Block
COMPILE::SWF {
    var sliderView:ISliderView = value.getBeadByType(ISliderView) as ISliderView;
    sliderView.thumb.addEventListener(MouseEvent.MOUSE_DOWN, thumbDownHandler);
    sliderView.track.addEventListener(MouseEvent.CLICK, trackClickHandler, false, 99999); 
}
COMPILE::JS {
    track = value.getBeadByType(
        org.apache.flex.html.staticControls.beads.SliderTrackView);
    thumb = value.getBeadByType(
        org.apache.flex.html.staticControls.beads.SliderThumbView);

    goog.events.listen(track.element, goog.events.EventType.CLICK,
                     handleTrackClick, false, this);

    goog.events.listen(thumb.element, goog.events.EventType.MOUSEDOWN,
                     handleThumbDown, false, this);

}

The purpose of the controller is to coordinate the input from the mouse with values from the model which are then reflected in the views. Here are the click handlers for the track:

Code Block
COMPILE::SWF
private function trackClickHandler( event:MouseEvent ) : void
{
	event.stopImmediatePropagation();

   var sliderView:ISliderView = _strand.getBeadByType(ISliderView) as ISliderView;

	var xloc:Number = event.localX;
	var p:Number = xloc/UIBase(_strand).width;
	var n:Number = p*(rangeModel.maximum - rangeModel.minimum) + rangeModel.minimum;


	rangeModel.value = n;

	IEventDispatcher(_strand).dispatchEvent(new Event("valueChange"));
}
 
COMPILE::JS
private function handleTrackClick(event:BrowserEvent):void
{
    var host:Slider = _strand as Slider;
    var xloc = event.clientX;
    var p = Math.min(1, xloc / parseInt(track.element.style.width, 10));
    var n = p * (host.get_maximum() - host.get_minimum()) +
          host.get_minimum();
 
    host.value = n;
 
    origin = parseInt(thumb.element.style.left, 10);
    position = parseInt(thumb.element.style.left, 10);
 
    calcValFromMousePosition(event, true);
 
    host.dispatchEvent(new org.apache.flex.events.Event('valueChanged'));
}

Using JavaScript Component Libraries with FlexJS

Many web applications now use JavaScript component sets, such as jQuery, and it is possible to use those frameworks with FlexJS; it is just a matter of changing how the components are built. The FlexJS package comes with a handful of jQuery-compatible and CreateJS-compatible components to get you started.

Panel

Remember that FlexJS works in both the ActionScript and JavaScript worlds. While you do not have to make ActionScript equivalents of your JavaScript components, it is good practice to provide them just so your application can be run in both environments. You will find ActionScript versions of the sample jQuery and CreateJS FlexJS components in the corresponding ActionScript packages: org/apache/flex/jquery and org/apache/flex/createjs, respectively.

jQuery for FlexJS

The biggest trick is to pull in the component set definition files. At this time, FlexJS does not have an easy way to do this. It is a manual process at this point:

  1. Compile your Flex application using Falcon JX.
  2. Open the index.html file that was generated.
  3. Insert the following lines into the <head> portion of the file.

    Code Block
    <link rel="stylesheet" href="http://code.jquery.com/ui/1.10.2/themes/smoothness/jquery-ui.css" />
    <script src="http://code.jquery.com/jquery-1.9.1.js"></script>
    <script src="http://code.jquery.com/ui/1.10.2/jquery-ui.js"></script>
    

    In the FlexJS directories you will find a small set of components in the org/apache/flex/html/jquery folder. If you compare these to the standard components in the html package, you will see there are few differences, the most obvious being the inclusion of jQuery function calls to modify the components.

Open the TextButton.as file and look for the createElement() and addedToParent functions in the COMPILE::JS block:

Code Block
/**
 * @flexjsignorecoercion org.apache.flex.core.WrappedHTMLElement
 */
COMPILE::JS
override protected function createElement():WrappedHTMLElement
{
	element = document.createElement('button') as WrappedHTMLElement;
	element.setAttribute('type', 'button');
	
	positioner = element;
	positioner.style.position = 'relative';
	element.flexjs_wrapper = this;
	return element;
}
 
COMPILE::JS
override public function addedToParent():void
{
	super.addedToParent();
	$(element).button();
}

The only difference between the jQuery version of the TextButton and the basic Text button is the jQuery button-making function, $(element).button(). Your jQuery components may differ greatly from their non-jQuery

Once you've created or converted the components to use jQuery, the JavaScript side of FlexJS can use jQuery functions to manipulate the components and their beads. For example, might want to create a bead that manipulates the styles and provides effects such as fades. Rather than modifying every component to have the ability to fade in and out, you can just add the "FadeBead" to a component strand.

CreateJS for FlexJS

CreateJS is an altogether different way of making components from jQuery. CreateJS is more like ActionScript in that you build things rather manipulate them. As with jQuery, the big trick is to bring in the CreateJS package:

  1. Compile your Flex application with the Falcon JX compiler.
  2. Open the JavaScript file that was generated from your main Flex application (e.g., if your Flex application's main file is Main.mxml, open Main.js).
  3. Insert the following lines before the goog.provide() statements:

    Code Block
    var mainjs = document.createElement('script');
    mainjs.src = './createjs/easeljs-0.6.0.min.js';
    document.head.appendChild(mainjs);
    
  4. Be sure to put a copy of the CreateJS component files in a sub-directory called, createjs, next to the other source directories.

If you open the TextButton.js file in the CreateJS package, you will find two definitions for the TextButton class. One in a COMPILE::SWF block and another in a COMPILE::JS block. This was done because the two versions have enough differences that it would be awkward to mingle the compiler directive blocks; it was cleaner to define the classes separately.

In this example, CreateJS functions construct a button-like object rather than use HTML elements. While the basic, jQuery, and CreateJS TextButtons diff in implementation, they are expressed exactly the same in MXML.

Using FlexJS jQuery and CreateJS Components in MXML

Once you've created your jQuery or CreateJS (or other component library) beads and components, you can add them to your Flex application MXML using namespaces. This is the root tag for a version of MyInitialView.mxml:

Code Block
<js:ViewBase xmlns:fx="http://ns.adobe.com/mxml/2009"
		xmlns:basic="library://ns.apache.org/flexjs/basic"
		xmlns:jquery="library://ns.apache.org/flexjs/jquery"
                xmlns:createjs="library://ns.apache.org/flexjs/createjs"
	>

Once you have declared the namespaces, you use the namespaces to identify the components:

Code Block
<!-- the basic tex button component -->
<js:TextButton label="Basic" />

<!-- the jQuery text button component -->
<jquery:TextButton label="jQuery" />

<!-- the CreateJS text button component -->
<createjs:TextButton label="CreateJS" />

Cross-Compiling

The ability to generate JavaScript code and components from ActionScript sources is built into the FlexJS development patterns. You guide the compilation process through the use of COMPILE::SWF (exclude from cross-compilation, SWF only) and COMPILE::JS (exclude from SWF, cross-compile to JavaScript) directives; any code not in these blocks is cross-compiled to JavaScript and winds up in the SWF. 

 There is nothing special about the process: create a Flex project the usual way and develop your component in ActionScript. Any framework elements you use will already have JavaScript equivalents. If you know you will need to refine the JavaScript result, then develop as much as possible in ActionScript and then do the cross-compile; the result will be well worth it as it will save you a lot of time making the JavaScript version.