Apache Royale Blog

Customization through the Royale API

This example shows you how to use the powerful Royale API to get access to the internal workings of components and customize them to suit your needs. As you can see, although Royale does a lot for you to simplify development, you always have full control of your code.

It uses the new Jewel UI set that supports themes and is available in the 0.9.4 release or later.

<?xml version="1.0" encoding="UTF-8"?>
<j:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
              xmlns:j="library://ns.apache.org/royale/jewel"
              xmlns:js="library://ns.apache.org/royale/basic"
              xmlns:html="library://ns.apache.org/royale/html"
              xmlns:models="models.*">

    <fx:Script>
        <![CDATA[
            import org.apache.royale.core.IBeadLayout;
            import org.apache.royale.core.IBeadView;
            import org.apache.royale.events.CloseEvent;
            import org.apache.royale.events.Event;
            import org.apache.royale.events.MouseEvent;
            import org.apache.royale.jewel.Alert;
            import org.apache.royale.jewel.CheckBox;
            import org.apache.royale.jewel.beads.layouts.HorizontalLayout;
            import org.apache.royale.jewel.beads.layouts.VerticalLayout;
            import org.apache.royale.jewel.beads.views.AlertView;

           private var alert:Alert;
           private var check:CheckBox;

           // Adding content to the Alert component and changing the ControlBar's Buttons Layout
           private function clickHandler(event:MouseEvent):void {
               alert = Alert.show("This example shows access to AlertView and ControlBar to add a CheckBox to the Alert's content area, expand the Button layout and change its defaults. The height of the alert is changed to 300px, too.", "Customized Alert Example", 3);
               alert.addEventListener(CloseEvent.CLOSE, alertClickHandler);
                alert.height = 300;

               check = new CheckBox();
               check.selected = true;
               check.text = "Buttons must fill the ControlBar's available space";
               check.addEventListener(Event.CHANGE, expandButtons);

               expandButtons();
           }
           
           private function expandButtons(event:Event = null):void {
               var alertView:AlertView = alert.getBeadByType(IBeadView) as AlertView;

               if(event == null)
               {
                   var verticalLayout:VerticalLayout = new VerticalLayout();
                   verticalLayout.gap = 9;
                   alertView.content.addBead(verticalLayout);

                   alertView.content.addElement(check);
               }

               var layout:HorizontalLayout = alertView.controlBar.getBeadByType(IBeadLayout) as HorizontalLayout;
               layout.itemsExpand = check.selected;
           }

           // Event handler function for displaying the selected Alert button.
           private function alertClickHandler(event:CloseEvent):void {
               alert.removeEventListener(CloseEvent.CLOSE, alertClickHandler);

               if (event.detail == Alert.YES)
                   status.text="You answered Yes";
               else
                   status.text="You answered No";
           }
        ]]>
    </fx:Script>

    <j:initialView>
        <j:View>
            <j:beads>
                <j:HorizontalCenteredLayout/>
            </j:beads>

            <j:Card width="350">
                <html:H3 text="Customization through Royale API"/>
                
                <j:Label text="This is a complex example that adds and retrieves beads at runtime. Click the button below to display an Alert window that adds content and makes changes in some parts of the default layout."
                       multiline="true"/>
                <j:Button text="Click Me" click="clickHandler(event)"/>
                <j:Label id="status"/>
            </j:Card>
        </j:View>
    </j:initialView>

</j:Application>

This example takes the Using the Jewel Alert Control example and uses the Royale API to add content and customize some parts of the Alert.

The code is more complex than in some of our other examples, for teaching purposes:

  • In the clickHandler method, we create an Alert control and create a CheckBox to add to the Alert's content zone. Then we call the expandButtons method to end initial customization.
  • The expandButtons method is where the heavy work of the Royale API happens:
    • Since all components in Royale are "composed" through the Strand/Bead API, we want to access the view part of the Alert; in this case we're talking about AlertView.
    • We can access AlertView with getBeadByType that retrieves a bead by its type.
    • Then, for learning purposes, we create a VerticalLayout bead and add it to the Alert's content using the addBead method (you can investigate other methods in the API like removeBead as well).
    • We add the CheckBox created in the previous method to the Alert's content. Since this involves a call-back method and an initialization method, we only want to add the checkbox at initialization time and not each time the user clicks the CheckBox.
    • Finally, we retrieve the ControlBar's default HorizontalLayout and customize it to expand its items (the buttons) to fill all the available space in the control bar. We use getBeadByType again to reference the layout, and then use a method available in most Jewel Layouts called itemsExpand that expect a Boolean. When this method is set to "true" all items in the layout expand to use all available space.

Where to go from here

The result of this code snippet is the following:

(We're using an iframe to host the actual results of this example compilation. To see the example in a separate window click this link.)

Full project with source code can be found here:

Project Source Code

Using an Item Renderer with a List

This example shows how to use an ItemRenderer to display the items in a List in your Royale application. It uses the new Jewel UI set that supports themes and is available in the 0.9.4 release or later.

<?xml version="1.0" encoding="UTF-8"?>
<j:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
              xmlns:j="library://ns.apache.org/royale/jewel"
              xmlns:js="library://ns.apache.org/royale/basic"
              xmlns:html="library://ns.apache.org/royale/html"
              xmlns:models="models.*">

    <fx:Style>
        @namespace "http://www.w3.org/1999/xhtml";
        @namespace j "library://ns.apache.org/royale/jewel";

        .iconListItemRenderer
        {
            IItemRenderer: ClassReference("itemRenderers.IconListItemRenderer");
        }
        .iconListItemRenderer .fonticon
        {
            margin-right: 24px;
        }
    </fx:Style>

    <j:model>
        <models:ListsModel id="listModel"/>
    </j:model>

    <j:initialView>
        <j:View>
            <j:beads>
                <j:VerticalCenteredLayout/>
            </j:beads>

            <j:Card width="340">
                <j:CardHeader>
                    <html:H3 text="Jewel List With ItemRenderer" className="primary-normal"/>
                </j:CardHeader>
                <j:CardPrimaryContent>
                
                    <j:List width="100%" height="300" className="iconListItemRenderer">
                        <j:beads>
                            <js:ConstantBinding
                               sourceID="listModel"
                               sourcePropertyName="iconListData"
                               destinationPropertyName="dataProvider"/>
                        </j:beads>
                    </j:List>
                    
                </j:CardPrimaryContent>
            </j:Card>
        </j:View>
    </j:initialView>

</j:Application>

By default, Apache Royale Jewel List-based controls display their data as plain text using ListItemRenderer. But Royale is capable of much more, and you can extend ListItemRenderer to display the items in your list in a pleasing and user-friendly way.

In this example we created an IconListItemRenderer in mxml that extends ListItemRenderer:

<?xml version="1.0" encoding="utf-8"?>
<j:ListItemRenderer xmlns:fx="http://ns.adobe.com/mxml/2009"
                    xmlns:j="library://ns.apache.org/royale/jewel"
                    xmlns:js="library://ns.apache.org/royale/basic"
                    xmlns:html="library://ns.apache.org/royale/html">

    <fx:Script>
        <![CDATA[
            import vos.IconListVO;
            
            [Bindable("dataChange")]
            public function get iconList():IconListVO
            {
                return data as IconListVO;
            }
        ]]>
    </fx:Script>

    <j:beads>
        <js:ItemRendererDataBinding />
        <j:HorizontalLayout gap="3" itemsVerticalAlign="itemsCenter"/>
    </j:beads>
    
    <js:MaterialIcon text="{iconList ? iconList.icon : ''}" visible="{iconList ? iconList.icon != null : false}"/>

    <html:Span text="{iconList ? iconList.label : ''}"/>
    
</j:ListItemRenderer>

The list gets its items from the data provider and passes them through the item renderer. The item renderer models each item in a consistent way: it adds a FontIcon to display an appropriate icon (selected, in this case, from the Google Material Icons font) at the left, and the text of the item in a Label to the right. To manage the components for each list item, you can use absolute positioning, layout classes or CSS. We used CSS in the fx:Style section in the main file for simplicity. As you see, the item renderer is declared in CSS as well, and is located in the itemRenderers package.

Note that we use Data Binding, deploying ItemRendererDataBinding. Data binding in Royale is not available by default to reduce code size. You add it where you need it, following the PAYG principle in Royale: Pay As You Go. We add a data binding bead here since we need the function, and not anywhere we don't need it.

The data used in the List control dataProvider is declared in the ActionScript 3 ListsModel class:

package models
{
    import org.apache.royale.collections.ArrayList;
    import vos.IconListVO;

    public class ListsModel 
    {
        /**
         * this is the dataProvider for the List
         */
        private var _iconListData:ArrayList = new ArrayList([
            new IconListVO("Alert", MaterialIconType.WEB_ASSET),
            new IconListVO("Button", MaterialIconType.CROP_7_5),
            new IconListVO("DropDownList", MaterialIconType.CREDIT_CARD),
            new IconListVO("CheckBox", MaterialIconType.CHECK_BOX),
            new IconListVO("Label", MaterialIconType.LABEL),
            new IconListVO("List", MaterialIconType.LIST_ALT),
            new IconListVO("RadioButton", MaterialIconType.RADIO_BUTTON_CHECKED),
            new IconListVO("Slider", MaterialIconType.STORAGE),
            new IconListVO("Text", MaterialIconType.SUBJECT),
            new IconListVO("TextInput", MaterialIconType.TEXT_FIELDS)            
        ]);

        public function get iconListData():ArrayList
        {
            return _iconListData;
        }
    }
}

The MaterialIconType class uses the icon names as they appear in the Material Icons font for convenience and to avoid typos. One additional benefit of using MaterialIconType in your code is that it injects the font into your html directly, without you having to deal with that step.

This class uses a data object (DOT, or POJO, depending on how you name this kind of code), called IconListVO to instance each piece of data that will appear in the List control.

Here's the result of this code snippet:

package vos
{
    [Bindable]
    public class IconListVO
    {
        public var label:String;
        public var icon:String;

        public function IconListVO(label:String, icon:String = null)
        {
            this.label = label;
            this.icon = icon;
        }
    }
}

In the main file, note how we link the ListsModel class through the model variable accessible throughout Royale to make it easy to link a model as a bead.

Finally, we link the model data to the list dataProvider using data binding, with ApplicationDataBinding (since we are at the Application level) and the ConstantBinding class that knows where the data is located (sourceID), what property holds it (iconListData) and where to inject the data (destinationPropertyName).

Hope you like this example 🙂

Where to go from here

The result of this code snippet is the following:

(We're using an iframe to host the actual results of this example compilation. To see the example in a separate window click this link.)

Full project with source code can be found here:

Project Source Code

Using View States to show or hide content

This example shows you how to display or hide content in your Royale application, thanks to the View States feature. It uses the new Jewel UI set that supports themes and is available in the 0.9.4 release or later.

<?xml version="1.0" encoding="UTF-8"?>
<j:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
              xmlns:j="library://ns.apache.org/royale/jewel"
              xmlns:js="library://ns.apache.org/royale/basic"
              xmlns:html="library://ns.apache.org/royale/html">
    
    <j:initialView>
        <j:View id="view">
            <j:states>
                <js:State name="login" />        
                <js:State name="loggedIn" />        
            </j:states>
            
            <j:beads>
                <js:SimpleStatesImpl/>
            </j:beads>

            <j:Card id="loginForm" includeIn="login">
                <html:H1 text="Royale login"/>
                <j:TextInput id="username" text="someuser"/>
                <j:TextInput id="password" text="somepass">
                    <j:beads>
                        <j:PasswordInput/>
                    </j:beads>
                </j:TextInput>
                <j:Button text="Login" emphasis="primary" click="view.currentState = 'loggedIn'" />
            </j:Card>

            <j:Card id="loggedInForm" includeIn="loggedIn">
                <html:H1 text="You are logged in!! :)"/>
                <j:Button text="Logout" click="view.currentState = 'login'"/>
            </j:Card>
        </j:View>
    </j:initialView>

</j:Application>

The View States feature is a way of putting different filters over part of the application so that different things appear depending on what the app is doing, what permissions the user has, what the user has just done, or some other condition. You create a series of "states" and associate components of your application with one or more of the states. The way you associate a component with a state is to add the includeIn attribute to the component and set the attribute equal to one or more of the available states. Once you have associated a parent component to a state, all of its children (the things "inside" it) are also associated to the state.

Using states gives you a lightweight and quick way of updating what the user sees, without having to provide all sorts of modules that have to be loaded and unloaded.

The example shows using states to switch between two Card components, what you see before logging in and what you see once you have logged in. To make this work you need to add the SimpleStatesImpl bead. Then define the states you need in the State class. Here, our two states are login and loggedIn. Finally, add an "includeIn" attribute to each Card component and associate it with a state.

When you first see the app, the currentState in the view is the first state listed in the State class: login.

When you click the button, the "click" function assigns a new state as the currentState. Anything that is not associated with the new state magically disappears, and anything that is associated with it is suddenly visible.

Instead of using includeIn, you have another way via dot notation in attributes, in this case notice the notation visible="true" and visible.<state>="false" in the following code:

<j:Card id="loginForm" visible="true" visible.loggedIn="false">
    <html:H1 text="Royale login"/>
    <j:TextInput id="username" text="someuser"/>
    <j:TextInput id="password" text="somepass">
        <j:beads>
            <j:PasswordInput/>
        </j:beads>
    </j:TextInput>
    <j:Button text="Login" emphasis="primary" click="view.currentState = 'loggedIn'" />
</j:Card>

<j:Card id="loggedInForm" visible="false" visible.loggedIn="true">
    <html:H1 text="You are logged in!! :)"/>
    <j:Button text="Logout" click="view.currentState = 'login'"/>
</j:Card>

You can use it in almost any attribute you want, not only visible.

Another way to set SimpleStatesImpl is vía CSS. You can do this in MXML or in an external CSS file:

@namespace "//www.w3.org/1999/xhtml";
@namespace js "library://ns.apache.org/royale/basic";
    
global {
    IStatesImpl: ClassReference("org.apache.royale.core.SimpleStatesImpl");
}

Where to go from here

The result of this code snippet is the following:

(We're using an iframe to host the actual results of this example compilation. To see the example in a separate window click this link.)

Full project with source code can be found here:

Project Source Code

Newer Posts Older Posts