Apache Royale Blog

Dividing an Apache Royale application with modules

This example shows how you can break an Apache Royale application into parts called Modules. You then have a main application that acts as a shell for the modules, that hold key features or functions, and that are loaded on demand, not when the user first opens the application.

In this way you benefit from better load times for your application since you don't load all the code at once. Another benefit is better compile times, since you have to build the application or module to which you have made changes, so you compile less code.

The application uses a ModuleLoader to load each module as it is needed. ModuleLoader can load a module directly (this is the default behavior), or you can turn off automatic loading by setting autoLoad to false, and perform the load of each module on demand.

This example shows a Jewel Card with a ModuleLoader and a button that triggers the load. You can push the button and see the sample Module load.

The Main Application

This is the code for the main application that holds the ModuleLoader:

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

    <fx:Script>
        <![CDATA[
           public function loadModule():void
           {
               moduleLoader.loadModule();
           }
        ]]>
    </fx:Script>
    
    <j:beads>
        <js:ApplicationDataBinding />
    </j:beads>

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

            <j:Card percentWidth="90">
                <html:H3 text="Dividing an Apache Royale application with modules"/>
                
                <j:Label html="This example uses Modules to load parts of the application"/>

                <j:ModuleLoader localId="moduleLoader" autoLoad="false"
                               modulePath="modules" moduleName="JewelModule"/>

                <j:Button text="Load a Module" emphasis="primary" click="loadModule()"/>
             </j:Card>
        </j:View>
    </j:initialView>
</j:Application>

As you can see, the ModuleLoader needs to know the modulePath (the path where the module can be found) and the moduleName (the name of the module to load). We set autoLoad to false to avoid having the module load when ModuleLoader is added to the application. The module loads when a user pushes the button that calls moduleLoader.loadModule(). That's all!

The Sample Module

This is the code for the Module:

<j:Module xmlns:fx="http://ns.adobe.com/mxml/2009"
        xmlns:j="library://ns.apache.org/royale/jewel"
        xmlns:js="library://ns.apache.org/royale/basic"
        initComplete="initModule()">
    
    <fx:Script>
        <![CDATA[
            private function initModule():void
            {
                trace("Initialize the module!");
            }
        ]]>
    </fx:Script>

    <j:beads>
        <js:ContainerDataBinding/>
        <j:VerticalLayout/>
    </j:beads>
    
    <j:valuesImpl>
        <js:SimpleCSSValuesImpl/>
    </j:valuesImpl>
    
    <j:HGroup gap="3">
        <j:IconTextInput>
            <j:beads>
                <j:TextPrompt prompt="Search..."/>
                <j:SizeControl size="xlarge"/>
            </j:beads>
            <j:icon>
                <js:FontIcon text="{MaterialIconType.SEARCH}" material="true"/>
            </j:icon>
        </j:IconTextInput>
        <j:Button text="Search" emphasis="secondary">
            <j:beads>
                <j:SizeControl size="xlarge"/>
            </j:beads>
        </j:Button>
    </j:HGroup>
    
</j:Module>

We need to use the Module tag as the main tag. This means that the module can't be loaded on its own. It needs to be parented by an application and loaded by a ModuleLoader.

The content of the module can be whatever code you want. In this case we are adding just a few Jewel controls to show a big search box.

Notice that the module has an initComplete handler where you can execute code you need to initialize the module (in this case just a log a message). This is similar to the applicationComplete feature for the application itself.

Since this particular module uses a search icon that comes from MaterialIconType, when you add it directly to an application, the compiler injects a link to make Material Icons available. Since this class is used in a module, not in the application itself, Royale adds it via JavaScript to the html head when the module is loaded, so it is ready to be added to the application.

Project structure

We have multiple compilation units (one application and one or more modules), building this application is more complex than a simpler application. You used to have a project for the application and a project for the module. You would like to copy the module output files into the target application folder to make the module available for the compiled application. In this example, Maven helps you by providing a maven build layout for an application and its modules and copying the files you need to the right folders for both debug and release compilations. Check the project source code below.

A simpler option is to have only one project with application and modules all together. This is how Tour De Flex is done. For this layout you must use module-output compiler option so royale can output modules in the folder you prefer. Check Apache Royale Modules documentation page to know more about it.

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 external JavaScript libraries in Apache Royale

This example shows how to use external JavaScript libraries in your Apache Royale application. You can add quick functionality to your application by including code that is not part of Apache Royale itself or is even not written in ActionScript.

In this way, you get lots of libraries available for free in the Internet to strengthen and extend your Apache Royale Application.

It also allows an IDE to provide code completion, type checking, etc.

The example shows a Jewel Card with a code text zone that loads an ActionScript code example. Click the "highlight block" button to show the code in a beautifully colored way, thanks to processing by a highlight library which is an external JavaScript library.

The JavaScript library used to show this feature is highlightjs. In JavaScript this library creates the hljs object our code references.

How to use JavaScript external libraries

We have two solutions available for using external JavaScript libraries in Apache Royale. We'll focus first on the better and recommended way, which is using the @externs compiler directive.

This method gives you robust access to JavaScript methods though ActionScript with dot access syntax (and lets you use code hinting in your IDE). But if you need to prototype something quickly, you can use dynamic syntax with bracket access notation.

Dot access

This is the recommended way. You get all advantages of an object-oriented language like ActionScript 3: type checking, compiler errors and warnings, and code hinting and code completion in your favorite IDEs.

The code for this hljs as3 stub code is located in this blog example project and is the @externs class definition for hljs:

////////////////////////////////////////////////////////////////////////////////
//
//  Licensed to the Apache Software Foundation (ASF) under one or more
//  contributor license agreements.  See the NOTICE file distributed with
//  this work for additional information regarding copyright ownership.
//  The ASF licenses this file to You under the Apache License, Version 2.0
//  (the "License"); you may not use this file except in compliance with
//  the License.  You may obtain a copy of the License at
//
//      //www.apache.org/licenses/LICENSE-2.0
//
//  Unless required by applicable law or agreed to in writing, software
//  distributed under the License is distributed on an "AS IS" BASIS,
//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//  See the License for the specific language governing permissions and
//  limitations under the License.
//
////////////////////////////////////////////////////////////////////////////////
package
{
    /**
     * @externs
     */
    COMPILE::JS
    public class hljs
    {
        /** 
         * <inject_script>
         * var script = document.createElement("script");
         * script.setAttribute("src", "https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.12.0/highlight.min.js");
         * document.head.appendChild(script);
         * var link = document.createElement("link");
         * link.setAttribute("rel", "stylesheet");
         * link.setAttribute("type", "text/css");
         * link.setAttribute("href", "https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.12.0/styles/atom-one-dark.min.css");
         * document.head.appendChild(link);
         * </inject_script>
         */
        public function hljs(){}

        public static function highlightBlock(block:Element):void {}
    }
}

You can see two main things in this code:

  1. An inject_html directive declared in the constructor adds the following lines to the html template, so you do not need to add the lines manually. If you use this library, Royale adds these references automatically, and if you remove all references, Royale removes the dependencies to the JavaScript library and nothing is output in the html file.
  2. Use the highlightBlock static function, which you can access as a normal method in the AS3 hljs class.

Bracket access

If you need to prototype something quickly you can use this method but remember we don't recommend that you use this in your main Apache Royale project.

First, reference the JavaScript library (and/or css if it exists) in your html template. For hljs you can copy the following lines:

<script src="//cdnjs.cloudflare.com/ajax/libs/highlight.js/9.12.0/highlight.min.js"></script>
<link rel="stylesheet" title="Atom One Dark" href="//cdnjs.cloudflare.com/ajax/libs/highlight.js/9.12.0/styles/atom-one-dark.min.css">

Then you can start using the library in your code. An example with the highlightjs hljs object is:

var hljs:Object = window["hljs"];
hljs["highlightBlock"](block);

As you can see, this avoids using a more structured language like ActionScript 3. You lose type checking and the compiler will not help you if you write something wrong. Plus, if the API is changed, that code will not be able to warn you about the changes.

The example

The example uses HTTPService to retrieve sample code and display it in an html:Code component. When the application loads, it fires the initialize event. We use that to order HTTPService to load the text file. When the file finishes loading, HTTPService fires a complete event. We use that to add the text file content to the code_txt String variable.

Note: Since the code_txt variable uses data binding (it's marked with the Bindable metadata and we prepared the application to handle data binding with the ApplicationDataBinding bead), the application fills the html:Code sourceCodeMXMLText with the loaded text.

This is the code for this example:

<?xml version="1.0" encoding="UTF-8"?>
<!--

 Licensed to the Apache Software Foundation (ASF) under one or more
 contributor license agreements.  See the NOTICE file distributed with
 this work for additional information regarding copyright ownership.
 The ASF licenses this file to You under the Apache License, Version 2.0
 (the "License"); you may not use this file except in compliance with
 the License.  You may obtain a copy of the License at

     http://www.apache.org/licenses/LICENSE-2.0

 Unless required by applicable law or agreed to in writing, software
 distributed under the License is distributed on an "AS IS" BASIS,
 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 See the License for the specific language governing permissions and
 limitations under the License.

-->
<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"
              initialize="codeTextLoader.send();">

    <fx:Script>
        <![CDATA[
           [Bindable]
           public var code_txt:String;

           public function highLightContent():void
           {
                COMPILE::JS
                {
                    hljs.highlightBlock(sourceCodeMXMLText.element);   
                }
           }
        ]]>
    </fx:Script>
    
    <j:beads>
        <js:ApplicationDataBinding/>
        <js:HTTPService id="codeTextLoader" url="as3code.txt" complete="code_txt = codeTextLoader.data;"/>
    </j:beads>

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

            <j:Card width="90%">
                <html:H3 text="Using external JavaScript Libraries"/>
                
                <j:Label html="This example uses hljs library to highligh a piece of code"/>

                <html:Pre height="300" width="100%" style="background-color: white">
                    <html:beads>
                        <j:ScrollingViewport/>
                    </html:beads>
                    <html:Code id="sourceCodeMXMLText" text="{code_txt}"/>
                </html:Pre>
                
                <j:Button text="highlight Block" emphasis="primary" click="highLightContent()"/>
             </j:Card>
        </j:View>
    </j:initialView>
</j:Application>

In the example code you can see how we call the hljs.highlightBlock method with the recommended dot syntax as with any other ActionScript code, creating a seamless integration between your project code and the external JavaScript code.

Conclusion

You can see how simple and elegant it can be to use external JS code, while not compromising the safe syntax you have when using the MXML and AS3 languages, to give you more dynamic options for your application at no cost.

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

Apache Royale v0.9.4 released!

The Apache Royale community is pleased to announce the release of Apache Royale 0.9.4.

The Apache Royale project is a continuation of the previous effort called FlexJS to produce a next-generation of the Apache Flex SDK that enables developers to use MXML and ActionScript to generate HTML/JS/CSS applications which can run natively in browsers. The cross-compiled code can also be used in Apache Cordova (Adobe PhoneGap) mobile applications.

This release should be considered 'beta' quality. The purpose of this release is to gather feedback about the features and implementation strategies, and to recruit new contributors. We hope to grow the code base into an SDK and tool chain that deliver the highest productivity when developing applications that can run on many platforms. Beta releases may not handle production needs.

In 0.9.4 you can find important additions like a full new UI set called Jewel that's ready for production. This new set was designed with look and feel / themes in mind, so you can have a cool interface out of the box just using Jewel. Another great addition is bringing full AMF/RemoteObject support to Apache Royale so you can ease your migration from Apache Flex.

We are also working hard on MX and Spark emulation components that will help make your migration of an existing Apache Flex application a breeze. Many people are contributing to this effort, but more are welcome: please help us develop this powerful feature.

Changes in 0.9.4:

Known Issues:

  • Users only using Basic components and not MXRoyale or SparkRoyale emulation components should delete frameworks/libs/MXRoyale.swc, frameworks/libs/SparkRoyale.swc, frameworks/js/libs/MXRoyaleJS.swc, and frameworks/js/libs/SparkRoyaleJS.swc from their library paths (or from the file system).

Updates to the RELEASE_NOTES discovered after this file was packaged into the release artifacts can be found here:

https://github.com/apache/royale-asjs/wiki/Release-Notes-0.9.4

You can see more here.

You can download a binary distribution, the source code or browse our GitHub repositories. If you're a NPM user you can check Apache Royale at NPM.

As well, you can help us filing bugs in the framework or compiler.

For questions about how to use Royale, send email to mailto:users@royale.apache.org. For questions and feedback on the development of the source code in the release, send email to dev@royale.apache.org.

Enjoy! 🙂

Newer Posts Older Posts