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

Compare with Current View Page History

Version 1 Next »

This page is not about "coding conventions" like where to put your curly braces.  

This page is about how a single .as file produces a component that runs in a SWF as well as JS in the browser (or other JS runtime).

Introduction

Here are some things to keep in mind when writing components for FlexJS.  Note that these considerations should not be needed for developing applications for FlexJS as the components should abstract away these issues so application developers don’t have to know about them.
Component development should have some similarities to developing components for the Flex SDK.  You create a Library Project and/or use compc to compile it into a SWC.  You’ll probably extend some base class.  You still need Event metadata if you are dispatching events.
The main difference is that there are different base classes and events, but also: you may need to write code that only works in the SWF or in JavaScript in the browser.  If you are lucky, your component will not need platform-specific code.  In fact, many components like DataGrid and Charts have no platform-specific code.  The platform-specific stuff tends to be in the low-level components like Button.  In more detail:

Conditional Compilation

We use conditional compilation to specify platform-specific code so that both the SWF and JS versions can be compiled from the same .as file.  Thus there is only one org.apache.flex.html.Button.as that gets compiled into a SWF/SWC and cross-compiled into a .JS (and included in the same SWC for use by FalconJX).
We have two conditional compilation flags:  COMPILE::AS3 and COMPILE::JS

Base Classes

SWF UI classes do not have one common UI base class.  That’s because Flash has flash.display.Sprite, flash.text.TextField, and flash.display.SimpleButton all of which have different API surfaces.  Yes, they all extend flash.display.DisplayObject, but org.apache.flex.html.Button does not extend org.apache.flex.html.UIBase like org.apache.flex.html.List does.  You should not use DisplayObject in code you want to cross-compile, so the only common type to reference “any” FlexJS component is the interface org.apache.flex.core.IUIBase.
The JS version of UI classes almost all extend org.apache.flex.core.UIBase.

Different inheritance

The section on Base Classes, plus the desire to abstract away platform differences, means that the JS version of a class may extend a different base class than the SWF version.
For example, for SWF, org.apache.flex.html.Button extends org.apache.flex.core.UIButtonBase, but in JS, it extends org.apache.flex.core.UIBase.
The source code for Button and other components with platform-dependent base classes is to simply use conditional compilation to put two class definitions in the same .as file.  So, for Button.as, it will start with something like:

 

/**
 * class documentation
 */
COMPILE::AS3
public class Button extends UIButtonBase
{
}
…
COMPILE::JS
Public class Button extends UIBase
{
}

 

When examining a source file, if you see a COMPILE::AS3 flag on the line before “public class”, you are pretty much guaranteed to find a COMPILE::JS flag later in the file.

Same Inheritance

The majority of UI components extend UIBase both for SWF and JS versions.  In those cases, there is no COMPILE::AS3 flag on the line before “public class”.  Instead COMPILE::AS3 and COMPILE::JS flags are scattered throughout the source file.  There are two patterns of usage:
1:  Per-definition.  Entire functions and vars (but not import statements) can be controlled by a conditional compilation flag of the form:

 

COMPILE::AS3
public var someVar:Object;


COMPILE::AS3
public function someFunction():void
{
}

 

2: Per-block.  Individual or groups of import statements and chunks of code in method bodies can be controlled by a conditional compilation flag of the form:

 

COMPILE::AS3
{
  import somePackage.someClass;
}


public function foo():String
{
    COMPILE::AS3
    {
       return “hi”;
    }
    COMPILE::JS
    {
        return “bye”;
    }
} 

 

Note that you will not get an error that a method like this doesn’t return a value because the conditional compilation essentially happens during the parsing phase so the compiler thinks the method looks like:

 

public function foo():String
{
    return “hi”;
} 

 

Or 

 

public function foo():String
{

    return “bye”;
}

 

JS Components Wrap HTMLElement, are DisplayObjects.

It has rarely been an issue, but keep in mind that in the SWF, the component IS the DOM object, but in JS, the component “wraps” or references the DOM object (an HTMLElement).  In theory, the APIs in the base classes for addEventListener, dispatchEvent and setting x,y, width, height abstract these differences away.

Wrap platform code if you can

When looking at all of the places where you have to inject conditional compilation, consider whether you can introduce a new helper class that wraps just enough platform-specific code that the main component will have no or much less conditional compilation that it would otherwise.  Examples are org.apache.flex.core.ApplicationBase, which greatly simplified org.apache.flex.core.Application, and org.apache.flex.effects.PlatformWiper which abstracts how to occlude a display object so the Wipe effects have no conditional compilation code in them.

Casting

You may find it annoying at times to have to add types to vars in JavaScript being ported back to ActionScript.  And once you do, you will start to get errors about missing properties.  Well, that’s the trade-off of type-checking.  Some time later you realize it will save you from leaving some other bug in the code.
In COMPILE::JS blocks you will find, for example, code that could look like:

 

 element = document.createElement(“div”);
 element.flexjs_wrapper = this;

 

 
But element is of type WrappedHTMLElement (so you can set the flexjs_wrapper property without an error) and document.createElement returns HTMLElement so you get an error and have to cast.  Try to use “as” casting as in:

 

element = document.createElement(“div”) as WrappedHTMLElement;

 

Now if you think about it, and know how “as” works in JS, you’ll realize that the “as” should fail in JS.  That’s because “as” works by examining data structures attached to the FlexJS classes that the actual HTMLElement won’t have.  So you have to do one more thing, which is tell the compiler to not generate the “as” code by adding in the asdoc for the method:

 

@flexjsignorecoercion org.apache.flex.core.WrappedHTMLElement

 

You’ll see in the code @flexjsignorecoercion for native HTMLElements as well.  There might be a better way to do this, but that’s what we’re using for now.
  • No labels