Strands and Beads
Adding functionality to a component through composition
Strands and beads are key concepts in Apache Royale, related to the PAYG (pay as you go) concept. The idea is to keep component code as lightweight as possible, and to add functionality and complexity only to the components that need it.
In the image, a TextInput is composed by a model (TextModel), a view (TextInputWithBorderView) and a controller (EditableTextKeyboardController). These are the minimun MVC beads to make it work for this component.
For example, you may use a lot of text input fields in your application, but only one or two need to be able to protect passwords by converting the display of text the user provides into dots. You may want to disable or enable some instances of the text input component, but not all of them, while an end user is working with your application. There is no reason to have that extra functionality (and added weight of code) available everywhere “just in case”, as was the rule in Apache Flex.
In the image we are composing (adding) two optional beads to the strand.
Every Royale component contains the minimum code necessary to perform its basic functions, and has “strands” onto which you can string “beads” of functionality that let an instance of the component do what you want it to do in a particular place in your application.
Finding the beads you need
Finding the beads that will provide what you want for a given component, or even knowing what beads are available in the official Apache Royale SDK can be a daunting task. There are hundreds of these little pieces of code distributed all over the framework code. For this reason we are gradually assembling a list of beads on each component page, so users can refer to a concrete strand (the component) and see a list of beads that work with it. Normally you can have:
- Specific Component Beads. (beads only usable for a specific component, like Jewel PasswordInput for an instance of Jewel TextInput)
- Common Shared Beads. Beads that can be used with more than one component, like Jewel Disabled bead, which can be used by Jewel Button, Jewel TextInput or Jewel CheckBox.
Adding a bead
There are three ways to add beads to a component: bake it into the code using <j:beads>
, declare it through CSS, or add it dynamically using addBead()
.
Adding a bead directly in the code
Each strand supports a beads
array and users can add beads in MXML.
In the following example we are using the Jewel TextInput strand to add Jewel TextPrompt and Jewel Disabled beads to the strand. Note that while TextPrompt
is specific to TextInput, you can use Disabled
with many Jewel components.
<j:TextInput text="Disabled with text...">
<j:beads>
<j:TextPrompt prompt="Disabled TextInput..."/>
<j:Disabled/>
</j:beads>
</j:TextInput>
Adding a bead through CSS
Adding beads through Cascading Style Sheets (CSS) is easy. You declare CSS rules where the selector is the strand and the declaration block has one or more delarations. Declarations can be bead declarations or standard CSS declarations separated by semicolons.
A bead declaration has the bead type as the property part (for instance, IBead, IBeadModel, and IBeadView) and a ClassReference
to the bead we want to assign as the value part.
Here is an example of a CSS bead declaration. We assign org.apache.royale.jewel.beads.views.ListView
as the bead view (IBeadView
) of the component strand:
IBeadView: ClassReference("org.apache.royale.jewel.beads.views.ListView");
Royale uses CSS to configure components with defaults beads. The following code snippet is the default initial bead configuration for a Jewel List and is declared though SASS (that gets compiled to CSS).
j|List
IBeadView: ClassReference("org.apache.royale.jewel.beads.views.ListView")
IBeadController: ClassReference("org.apache.royale.jewel.beads.controllers.ListSingleSelectionMouseController")
IBeadLayout: ClassReference("org.apache.royale.jewel.beads.layouts.VerticalLayout")
IItemRendererClassFactory: ClassReference("org.apache.royale.core.ItemRendererClassFactory")
IItemRenderer: ClassReference("org.apache.royale.jewel.itemRenderers.ListItemRenderer")
IViewport: ClassReference("org.apache.royale.jewel.supportClasses.scrollbar.ScrollingViewport")
IViewportModel: ClassReference("org.apache.royale.html.beads.models.ViewportModel")
IBeadModel: ClassReference("org.apache.royale.jewel.beads.models.ArrayListSelectionModel")
IDataProviderItemRendererMapper: ClassReference("org.apache.royale.jewel.beads.itemRenderers.DataItemRendererFactoryForCollectionView")
Royale developers can override default beads through their own coded CSS files that will take priority over framework CSS rules, or through AS3 and/or MXML code.
Adding a bead dynamically with addBead()
The third way to add a bead is though ActionScript using the addBead()
API method.
Here is example code:
// for the Jewel Alert component, retrieve the AlertView (IBeadView)
var alertView:AlertView = alert.getBeadByType(IBeadView) as AlertView;
// create a VerticalLayout (IBeadLayout)
var verticalLayout:VerticalLayout = new VerticalLayout();
verticalLayout.gap = 9;
// add the bead layout to the content part of the view layout
alertView.content.addBead(verticalLayout);
See a full example of the code above in the Customization through the Royale API example.
Accessing a bead from a strand
There are many cases where you need to find a specific bead without necessarily knowing the implementation. The two most common cases are accessing a strand’s view and model.
For these two cases, any class which implements IStrandWithModelAndView
or inherits from UIBase
(which is pretty much every visual component) has direct access to the view
and model
getters on the strand. These getters should always be used for views and models.
Sometimes, a component might have more than one model and you might not be sure if the one you want is the main one. In that case you should use the getModelByType(strand,classOrInterface)
function in the org.apache.royale.html.util
package. It will find the correct bead in the most efficient way it can.
For other bead types, you can use strand.getBeadByType(classOrInterface)
. It will find the bead you need from the beads which belong to the strand. You can use this for views although it’s less efficient than accessing the view directly. You should not use this for models because models are only loaded the first time they are accessed and if you use strand.getBeadByType(IBeadModel)
you will get null if the model has not yet been accessed.
Creating a bead
The following piece of code shows the most basic bead structure to use when you create a bead:
package beads
{
import org.apache.royale.core.IBead;
import org.apache.royale.core.IStrand;
import org.apache.royale.core.UIBase;
/**
* sample code for a bead class
*/
public class SomeBead implements IBead
{
protected var _strand:IStrand;
/**
* @copy org.apache.royale.core.IBead#strand
*
* @royaleignorecoercion org.apache.royale.core.UIBase;
*/
public function set strand(value:IStrand):void
{
_strand = value;
// do something
}
}
}
All beads implement the IBead interface (or a subclass like IBeadModel or IBeadView) and can refer to the strand component using the strand
method implementation of the IBead interface.
Beads can be tiny pieces of code so in this way we get great encapsulation of functionality with a few lines of code.
If you see the need for a bead that does not yet exist, you can create your own and contribute it to the Royale project. Information on the bead lifecycle is available at Creating Components.
Strand management
This section will include information on adding and removing beads and the significance of bead order