You are viewing an old version of this page. View the current version.

Compare with Current View Page History

« Previous Version 20 Next »

FlexJS Components

Components in the FlexJS framework are bundles of functionality formed by composition, rather than by inheritance. The concept of adding functionality (composition) is different from most other UI approaches which tend to extend (inheritance) functionality resulting in more complex components or adding features (overloading) to existing components.

For example, to have a text input field also include password and prompt features, you might use inheritance to create additional classes such as PasswordInput, PromptInput, and then PasswordAndPromptInput. You can see that this can quickly get out of control. Another option is to add these properties to the text input field and overload it with features. Feature overloading makes it convenient for developers but most of the time only a basic set of features are ever used. This means applications can be very large (byte-wise) because every component has code and properties it rarely needs.

FlexJS solves these problems by allowing developers to add just the features they need when they need them. Instead of every text input field having password and prompt capabilities, only a few text input controls have those features and the application remains lighter as a result.

Strands and Beads

A component in FlexJS consists of strands onto which beads are added. A strand is the component wrapper or framework while a bead encapsulates a particular bit of functionality. In the example above, the password and prompt features are beads that can be added to a text input component's strand. Further, the actual text input control is provided by a view bead and the data (the text itself) is managed by a model bead. In this way, components in FlexJS embody the model-view-controller (MVC) paradigm.

Beads can interact with each either through the strand, with events, or direct manipulation. For example, a bead that makes text red might listen for changes to the component's text input view bead and when it detects a key word, changes the text directly. Beads can add visual elements to components (view beads) or just change data or handle events.

A Word About ActionScript and JavaScript

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 code.

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 (the DOM); the FlexJS TextInput component is just a thin wrapper for the HTML control. When 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 style 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.

This example already exists in the FlexJS library. You can look at the class files and follow along.

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.

// actionscript
package org.apache.flex.html.staticControls.beads
{
...
    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.

// javascript
goog.provide('org.apache.flex.html.staticControls.beads.PasswordInputBead');
...
org.apache.flex.html.staticControls.beads.PasswordInputBead = function()

Implement the Strand Setter

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

// actionscript
public function set strand(value:IStrand):void
{
    _strand = value;
}
// javascript
PasswordBead.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:

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

There is no JavaScript equivalent for this step. 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:

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

The first thing the event handler does is retrieve the TextInputView bead from the strand using the getBeadByType() function. This function will examine the strand and return the first bead that matches the given type.

With the TextInputView 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:

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

so the set_strand function now reads:

// javascript
PasswordBead.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.

This 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".

Use the Bead

Updating the Manifest and Library

If your bead is to become part of the FlexJS framework library, you will need to modify additional files. If your bead is solely for your application use, you can skip this part.

Your bead must be compiled and place into the FlexJS SWC library. To do, find the FlexJSUIClasses.as file and add your bead. For example:

import org.apache.flex.html.staticControls.beads.PasswordInputBead; PasswordInputBead;

To allow your bead to use the FlexJS namespace, it must be present in the FlexJS manifest file. Find the basic-manifest.xml file and add the bead, such as:

<component id="PasswordInputBead" class="org.apache.flex.html.staticControls.beads.PasswordInputBead" />

MXML

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:

<basic:TextInput>
	<basic:beads>
		<basic:PasswordInputBead />
	</basic:beads>
</basic: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:

<basic:TextInput>
	<basic:beads>
		<basic:PasswordInputBead />
		<based:TextPromptBead prompt=”password” />
	</basic:beads>
</basic: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.

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 control 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 (e.g., a mouse control bead for a touch control bead).

Making a Component - ActionScript

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.

The Slider component is a good example of the MVC architecture of FlexJS components. The Slider has a view bead (which manages the track and thumb - also beads), a model bead (the RangeModel used by other components), and control bead (to handle mouse clicks and drags). Once you've decided what your component will do, break it down into those three parts. In general, your view and controller beads will probably be unique to your component but you might be able to re-use a model bead.

Using Slider as an example, the basic approach to building a FlexJS component is:

  • Slider.as (and Slider.js) is the strand and provides the UI display list. The strand part extends UIBase (for ActionScript) or creates the base element (for JavaScript) and defines the public properties and events for the component. In the case of Slider, it has property setters and getters for minimum, maximum, value, and snapInterval that pass through to the model. Slider also dispatches a valueChanged event.
  • SliderView.as is the view bead and provides the track and thumb, which are also beads; SliderView simply manages them.
  • SliderMouseController is the controller bead and watches for mouse events on the SliderView, translating them to model values and updating the view.
    The separation of the parts makes it easy for someone to replace them. For example, you could change how the Slider looks or behaves by replacing the view or controller beads.
  • RangeModel.as (and RangeModel.js) provide the data model for the component. The model has property setters and getters for minimum, maximum, value, and snapInterval which are called by the strand (Slider.as and Slider.js). Changes to any of the properties dispatch a corresponding change event.

One thing you will not see in the Slider.as (strand) code is the application or naming of any specific beads. The beads for a component are identified by the style for the bead. If you look at the defaults.css file, you'll find the Slider has the following style defined:

Slider
{
    IBeadModel: ClassReference("org.apache.flex.html.staticControls.beads.models.RangeModel");
    iBeadView:  ClassReference("org.apache.flex.html.staticControls.beads.SliderView");
    iBeadController: ClassReference("org.apache.flex.html.staticControls.beads.controllers.SliderMouseController");
    iThumbView: ClassReference("org.apache.flex.html.staticControls.beads.SliderThumbView");
    iTrackView: ClassReference("org.apache.flex.html.staticControls.beads.SliderTrackView");
}

A very powerful piece of FlexJS is the ValuesManager which connects components to styles and applies the beads. In this style definition for Slider, you can see which model, view, and controller are being applied. To replace something, just change it in the style. Since the SliderView uses beads for the thumb and track, those are also defined in the style so you can replace those elements, too.

When you make your own components, be sure to think about replacement - what parts of your component do you think someone might want to change or swap out - then place them into a style definition for your component.

Slider (strand)

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 need 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.

_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 not dispatched on the model, but on the strand. This helps other components listen for changes (they need only the component/strand) and allows the beads of the component to communicate. 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.

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. The controller sets up the mouse event handlers to do 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. 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.

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: This is a bead that listens for mouse 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).

// 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.

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.

 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.

  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.

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:

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'));

The first thing the component does is 

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.

FlexJS uses the Google Closure Library and as such, implicit setters do not work. For example, doing slider.value = 4, will not trigger the Slider's set_value() function. You must use the setter (and getter) functions explicitly: slider.set_value(4).

  • No labels