Skip to content
Frank Wienberg edited this page May 29, 2017 · 2 revisions

Ext Config Objects in MXML

Ext JS uses config object literals to declaratively specify (nested) UI components and other Ext-specific objects like layouts, stores, or applications. In FlExt AS, you can still use this declarative notion, because ActionScript also supports object literals. However, in ActionScript, object literals are untyped, resulting in no compiler type checks and - worse - no IDE support for this most important part of UI programming.

In Flex, the declarative part is done in MXML, so Jangaroo uses MXML instead of Config object literals to specify (Ext) component trees. Section [MXML] describes the subset of MXML Jangaroo uses for that purpose in detail. Here, it is shown how Ext JS Config objects map to MXML (and vice versa).

Recap: Ext JS Config Objects

To get a basic understanding of Ext JS Config objects, please consult the corresponding Sencha documentation page. In the following, a short summary is given that illustrates the concept, which may suffice to get the idea how Jangaroo MXML works.

A Config object describes which class to instantiate and which config options (properties) to set on the instance. In particular, certain config options can contain one config object or an array of config objects. For example, every Container has an items property that describes all nested components through an array of config objects.

For components, the class to instantiate is specified by the property xtype. There is a mapping from xtype identifier to the fully-qualified class name to use.

{
  xtype: "viewport",
  items: [{
    xtype: "panel",
    title: "Hello World!",
    tbar: {
      xtype: "toolbar",
      items: [{
        xtype: "button",
        text: "Click me!",
        handler: function onClick(button) {
          ...
        }
      }]
    }
  }]
}

In this example, a viewport is specified (mapping to class Ext.container.Viewport), containing a panel (Ext.panel.Panel) with a toolbar with one button using the text "Click me!" and a handler function that reacts on button clicks (left out here for brevity).

In Ext JS code examples, you'll often see config objects without any xtype. This is possible because all container classes can define a default xtype for their items, and also other properties assume a default type that makes sense. However, for readability and clarity, it is recommended to always specify the xtype.

To create the actual (component) instance from a config object, there are several options. A lot of Ext JS methods that take an instance also accept a config object, for example the method Ext.container.Container#add(). Consult the Ext JS API documentation to find out about these methods and their exact behavior). The most general way is to hand over the config object to the utility method Ext.create(cfg).

Representing Config Objects in MXML

The component tree from the previous example looks quite similar when specified in Jangaroo MXML:

<Viewport xmlns:fx="http://ns.adobe.com/mxml/2009"
          xmlns="exml:ext.config">
  <items>
    <Panel title="Hello World!">
      <tbar>
        <Toolbar>
          <items>
            <Button text="Click me!"
                    handler="function onClick(button) {
                      ...
                    }"/>
          </items>
        </Toolbar>
      </tbar>
    </Panel>
  </items>
</Viewport>

Note that

  • the xtype property value is converted to upper case (more precisely CamelCase) and used as the MXML node name, and
  • config properties are either turned to MXML attributes (like title, text and handler) or to MXML elements nested inside the object element (like items and tbar).

Using CamelCase for MXML elements denoting objects is best practice because it allows to easily distinguish them from property elements.

To transfer Ext JS mapping xtype values to classes, Jangaroo uses the Flex feature "component library". Jangaroo Ext AS defines ActionScript type wrappers for all Ext JS classes and a component library for all components with an xtype and all layouts and other classes with an alias under the namespace exml:ext.config.

Actually, in Ext JS, xtype: "foo" is just an abbreviation for alias: "widget.foo", so all components use the alias prefix widget.. Besides components, there are a few other types of classes that also use Ext's config system and declare an alias, using other alias prefixes like layout. or data.. Instead of introducing separate component libraries for each prefix, Jangaroo Ext AS chose to prefix the local names of all aliases that do not start with widget., so the Ext alias data.stringfield becomes the MXML element name data_StringField.

In Jangaroo's GitHub repository ext-as, you find the full mapping from local name to fully-qualified class name in Ext AS.

Accessing Classes By Fully-Qualified Name

In Ext JS, an alternative to using xtype or alias shortcuts is to use the fully-qualified name of a class and put it in the attribute xclass. This is not a bad choice, since for inheritance (extend) and require, you need to use (and thus know) the fully-qualified class names, anyway. And for your own classes, you can even refrain from declaring an xtype.

Using this technique, the previous example could be written like so:

{
  xclass: "Ext.container.Viewport",
  items: [{
    xclass: "Ext.panel.Panel",
    title: "Hello World!",
    tbar: {
      xclass: "Ext.toolbar.Toolbar",
      items: [{
        xclass: "Ext.button.Button",
        text: "Click me!",
        handler: function onClick(button) {
          ...
        }
      }]
    }
  }]
}

The MXML equivalent is to not use the Ext AS component library, but instead declare namespaces for ActionScript packages and combine them with the class names:

<container:Viewport xmlns:fx="http://ns.adobe.com/mxml/2009"
                    xmlns:container="ext.container.*"
                    xmlns:panel="ext.panel.*"
                    xmlns:toolbar="ext.toolbar.*"
                    xmlns:button="ext.button.*">
  <container:items>
    <panel:Panel title="Hello World!">
      <panel:tbar>
        <toolbar:Toolbar>
          <toolbar:items>
            <button:Button text="Click me!"
                           handler="function onClick(button) {
                             ...
                           }"/>
          </toolbar:items>
        </toolbar:Toolbar>
      </panel:tbar>
    </panel:Panel>
  </container:items>
</container:Viewport>

Of course, you can mix-and-match both techniques.

Ext AS Specialties

So far, all this is standard MXML as used by Flex. As said above, Jangaroo faces the challenge of using Ext JS components through Flex syntax, so this section discusses where this results in Ext-specific MXML constructs.

Ext Versus Flex Component Instantiation

One main difference between Ext and Flex is how components are instantiated.

In Flex, components have a no-arg constructor and properties that can be assigned one by one. Then, they can be added to some container component.

In Ext JS, all config options to be set on the component are collected in a simple config object. Then, the component's constructor is called with this config object, performing the complete initialization.

The following code fragments illustrate both approaches.

In "real" Flex, the ActionScript code generated from the MXML fragment from last section would look like so:

var button:Button = new Button();
button.title = "Click me!";
button.handler = ...;
var toolbar:Toolbar = new Toolbar();
toolbar.addChild(button);
var panel:Panel = new Panel();
panel.title = "Hello World!";
panel.tbar = toolbar;
var viewport:Viewport = new Viewport();
viewport.addChild(panel);

In Ext JS, one would use nested config objects and hand them in to Ext.create():

Ext.create({
  xclass: "Ext.container.Viewport",
  items: [{
    xclass: "Ext.panel.Panel",
    title: "Hello World!",
    tbar: {
      xclass: "Ext.toolbar.Toolbar",
      items: [{
        xclass: "Ext.button.Button",
        text: "Click me!",
        handler: function onClick(button) {
          ...
        }
      }]
    }
  }]
});

The subtle difference is the order in which nested components get instantiated. While Flex instantiates a sub-component, configures it, and then adds it to its container, Ext JS config objects are converted to instances starting at the container and continuing down to sub-components. The problem is that while theoretically, Ext JS components could be instantiated like Flex components from inner to outer, in practice, that does not work for some classes (some containers hand down a back-reference to their sub-components), and even if it did, such code would be very inefficient in Ext JS, since updating a property after initial creation is more expensive.

The consequence for Jangaroo is that is tries to compile MXML to Ext config objects, allowing for lazy instantiation of the corresponding objects. To know when to use config objects and when to instantiate objects immediately, Jangaroo uses custom annotations in the Ext AS API. You should keep this in mind when you observe an unexpected instantiation order.

Constructors

The most obvious and annoying difference resulting from the different approaches to instantiate objects with properties is that Ext AS and Flex constructors look different. They have a different signature, resulting in a different order of calling super and assigning properties.

In Ext AS:

public function MySubClass(config:Object) {
  config.color = "red";
  super(config);
}

In Flex:

public function MySubClass() {
  super();
  this.color = "red";
}

You see that in Flex, there is no such thing as a config constructor parameter. This becomes a problem when you want to use MXML to compute properties based on other config properties, like in this example:

public function MySubClass(config:Object) {
  config.fullName = config.firstName + " " + config.surname;
  super(config);
}

In Flex MXML, this could be expressed by using the object's own properties in a binding expression:

<ac:MyClass xmlns:fx="http://ns.adobe.com/mxml/2009"
            xmlns:ac="com.acme.*">
  <fx:Declarations>
    <fx:String id="fullName">{firstName + " " + surname}</fx:String>
  </fx:Declarations>
</ac:MyClass>

The semantic difference is that Flex binding expressions are not evaluated once, but every time the observable ("bindable") source properties change. Constructor code is obviously evaluated only once.

Ext AS Config Objects

To represent Ext JS config objects in ActionScript in a type-safe way, Jangaroo takes the approach to reuse the type of the resulting instance for config objects. In Jangaroo 2 (EXML), config API has been declared separately from instance API, resulting in an additional class for each Ext class that takes a config parameter. Jangaroo 2 was tailored for Ext JS 3.4, where config API and instance API were similar, but impossible to describe with one ActionScript class.

Things have changed with Ext JS 6, where the config system takes care of any config options being accessible on the instance, too. Such config options are called bindable in the Ext documentation, because the instance accessors allow to change them any time after creation.

Unfortunately, Ext's config system does not utilize JavaScript 5's accessor functions, but uses classic get/set methods. For example, declaring a config option foo would implicitly declare two methods getFoo and setFoo that have to be used to query and update the property after creation (especially after rendering).

ActionScript 3 supported property accessor functions right from the start (when JavaScript 5 was not yet implemented by all major browsers, especially IE lagged behind). Thus, in ActionScript, it feeld much more natural to use accessors than to use get/set methods. The most important advantage is that with accessors, reading and writing a property uses the name syntax, so an expression like myObj.foo can be used as-is on the left-hand side and on the right-hand side of an assignment. This again eases (two-way) property binding. Also, you as an application developer no longer have to use different syntax for initial configuration (Ext JS: config objects) and later modification (Ext JS: set-methods) of a component.

To give you full control in Ext AS over when to create a config object in contrast to directly instantiating the target class, Jangaroo allows a special kind of type cast. Type-casting primitive Objects into a type that is annotated with [ExtConfig] (including superclasses) is allowed and results in an xclass attribute with the fully-qualified target class name being added to the object.

import ext.button.Button;

var buttonConfig:Button = Button({});
// buttonConfig now is { xclass: "ext.button.Button" }
var button:Button = Ext.create(buttonConfig);

The advantage is that you can now assign properties in a type-safe way and with the same syntax, no matter whether the object is a config or an instance:

import ext.button.Button;

var buttonConfig:Button = Button({});
buttonConfig.text = "Click me!";
// buttonConfig now is { xclass: "ext.button.Button" }
var button:Button = Ext.create(buttonConfig);
button.addListener("click", function():void {
  button.text = "Thank you!";
  // Jangaroo takes care of the setText() method being called!
});

As you can see in this example, the button property text is assigned twice: First, on the config object that is used to create the button instance. Then, in the click event callback, the same property is set on the button instance. In Ext JavaScript, you would have to use button.setText("Thank you!") here.

Also, tying to set buttonConfig.foo = "FOO!" would result in a compile error, since ext.button.Button does not declare a property foo.

However, note that since Ext AS uses the same type for config object an instance, using buttonConfig.click() would not result in a compile error. When config properties are accessed, the compiler does not try to determine whether a variable points to a config object or an instance, but defers this decision to a run-time utility function (AS3.getBindable(), AS3.setBindable()). Thus, trying to invoke methods on config objects result in run-time errors, not compile errors.

Jangaroo MXML Config Constructor Parameter

To access the constructor parameter in Jangaroo MXML, since Flex does not support constructor parameters, you have to declare a private field config and a "native" constructor signature like so:

<fx:Script>
  private var config:MyClass;

  public native function MyClass(config:MyClass = null);
</fx:Script>

where MyClass is the name of the MXML class being declared. Remember, the config type is always the same as the instance type.

The constructor is declared native (and thus has no body) because the actual constructor code is generated from the declarative part of your MXML file.

Clone this wiki locally