Skip to content

Migrate Jangaroo 2 to 4

Frank Wienberg edited this page Nov 14, 2017 · 11 revisions

Migrating your Jangaroo 2 Application to Jangaroo 4

This page describes how to migrate your existing Jangaroo 2 Ext AS 3.4 application to Jangaroo 4 FlExt AS 6.2.

As the overview of the differences between Jangaroo 2 and 4 on page Home explains, while for Jangaroo 2, there is a standard library to use Ext JS 3.4, Jangaroo 4 is based upon Ext JS 6.2. Furthermore, while Jangaroo 2 uses EXML as a proprietary declarative language to specify Ext JS UIs, the new version 4 reuses MXML, the declarative language of Apache Flex.

When migrating a Jangaroo 2 Ext-JS-based application to Jangaroo 4, there are three major tasks:

  1. Migrate EXML to MXML
  2. Migrate Sencha Ext JS 3.4 API usages to Ext JS 6.2
  3. Manual migration (clean-up automatic migration, fix usages of changed Ext JS concepts)

Jangaroo provides migration tools to help you with the first two steps.

For migrating EXML to MXML, there is a command line migration tool, integrated into Jangaroo's Maven plugin.

For migrating Ext JS 3.4 API to version 6.2, Jangaroo provides an IntelliJ IDEA Plugin that takes advantage of IDEA's ActionScript and MXML capabilities to adjust your code accordingly.

The third step is a manual effort. All we can do is provide some hints at things you may want to clean up after automatic migration and what might go wrong when trying to run code tailored at Ext JS 3.4 with version 6.2.

Preparation

  1. Use IntelliJ IDEA Ultimate version 2016.3 and up (tested with 2016.3.3, 2016.3.7 and 2017.2.5).

  2. It's recommended to fix errors found by IntelliJ IDEA in your module before migration. To this end, with still having the old Jangaroo 0.9 plugin installed, select "Analyze | Inspect Code" in IDEA and fix reported errors in *.as and *.exml files as possible. Code that is red in Idea cannot be migrated correctly.

  3. It's also recommended to replace untyped API usages if possible. One easy-to-replace pattern are config object constructor calls that use untyped JSON objects. In IDEA, you can use "Find In Path" with case-sensitive regular expression new [a-z][a-zA-Z0-9]*\(\{ to find such patterns. For example, you could replace

    var foo:Foo = new Foo(new foo({ bar: 'value' }));
    

    with

    var fooConfig:foo = new foo();
    fooConfig.bar = 'value';
    var foo:Foo = new Foo(fooConfig);
    
  4. Install IDEA plugin Jangaroo 4 (and, if IDEA complains about missing dependent plugins, install these, too)

  5. Install IDEA plugin Jangaroo Migration to Ext AS 6

  6. We assume you use Git for version management. Other VCSs work analogously.

Migration Steps

  1. Make config constructor parameter optional for MXML

    • In IDEA, use "Replace In Path", check "Regex" and "Match case" and replace

      (public\s+function\s+[A-Z][A-Za-z0-9_]*\s*\(\s*[A-Za-z]+\s*:\s*[a-z][a-zA-Z0-9_.]*)(\s*\))

      with

      $1 = null$2

    • Make sure only constructors have been modified.

    • Commit these changes, e.g with git commit -a

  2. Build your Maven module, still using the old Jangaroo version, so that the target directory contains all generated classes afterwards. Generated config and properties classes are required by the following steps.

  3. Make EXML target classes that have the same name as the EXML file a baseClass of the EXML class

    mvn net.jangaroo:exml-maven-plugin:2.0.19:exml-target-to-base
    
  4. Rename EXML files to MXML files

    mvn net.jangaroo:exml-maven-plugin:2.0.19:convert-to-mxml -DrenameOnly
    
  5. Create a separate Git commit

    A separate commit is used to avoid that Git looses the history of files that have been renamed and will be modified.

    git add --all
    git commit
    
  6. Convert EXML to MXML

    First, find out the target Ext AS version to use. If you migrate a CoreMedia Studio project, determine the correct version using this table:

    CoreMedia Studio Version (CMS9 or LC3) Ext AS Version
    1701.x 6.2.0-6
    1704.x 6.2.0-6
    1707.x 6.2.0-13
    1710.x 6.2.0-14

    Alternatively, browse all available Ext AS versions here: http://repo1.maven.org/maven2/net/jangaroo/ext-as/

    Then, run the following Maven command, inserting your desired Ext AS version for x:

    mvn net.jangaroo:exml-maven-plugin:2.0.19:convert-to-mxml -DalreadyRenamed -DextAsVersion=6.2.0-x
    
  7. Use IDEA Plugin "Jangaroo Migration to Ext AS 6" to migrate API usages

    This step requires old generated config classes to be present in target/generated-sources. If you've followed all previous steps, these classes should be present, if the migrated modules contains config classes.

    • Open module in IDEA having "Jangaroo 4" and this plugin installed.

    • Make sure IDEA can access the Internet. This could be prevented by being offline or by incorrect IDEA Web proxy settings: Settings > Appearance & Behavior > System Settings > HTTP Proxy

    • Maven Reimport

    • Select "Refactor | Migrate to Ext AS 6..."

    • Enter the version of ext-as (as in the previous step), e.g. 6.2.0-6.

      Unfortunately, the IDEA-based migration tool is currently limited to Ext AS versions up to 6.2.0-12.

      Newer Ext AS versions have been released in SWC artifact format and currently cannot be used with the IDEA-based migration tool. However, there have been near zero API changes since 6.2.0-12, so using this version even if your migration target is a newer version should not cause problems. After migration, using new versions with SWC artifacts works as expected.

    • A bubble tells you that the ext-as artifact and some transitive dependencies have been downloaded

    • Usages of changed API elements will be displayed

    • Click "Do refactor"

    • Afterwards, check if messages "REPEAT the refactoring" show up in IDEA's event log. If so, repeat with "Refactor | Migrate to Ext AS 6..." until no such messages show up anymore.

  8. Change your module pom.xml to use up-to-date version of jangaroo-tools and ext-as

    • Maven Reimport in IDEA
    • Make sure that IDEA really uses the new versions in its project structure
  9. Migrate usages of generated properties classes

    This step requires old generated properties classes to be present in target/generated-sources. If you've followed all previous steps, these classes should be present, if the migrated module contains *.properties files.

    • Select "Refactor | Migrate Ext AS Properties"
    • Usages of properties files are displayed
    • Click "Do refactor"
    • Afterwards, check if messages "REPEAT the refactoring" show up in IDEA's event log. If so, repeat with "Refactor | Migrate Ext AS Properties" until no such messages show up anymore.
  10. Optimize Imports in AS and MXML Files

  11. Commit the changes and start to fix remaining problems manually

Migration Aftermath

This section documents migration-relevant changes caused by the change from EXML to MXML, changes in the Jangaroo runtime and in the Ext AS API that are not covered by the automatic migration tools.

For the manual effort to actually make your Jangaroo application work with Ext JS 6.2 again, also check Migrate Ext JS 3.4 to 6.2.

Pull Conflicting or Redundant Config Properties to the Base Class

With the introduction of MXML, config options are now properties of the target class, and config classes (the lower-case classes in the module's config package) no longer exist (see MXML).

While the Jangaroo migration tool takes care of adding config options to MXML classes, there is an outdated "best practice" that the base class (superclass) of that former EXML class already defines a private member of the same name and in the constructor code, copies config properties from the config object (which now has the target class as its type) into its private member, like so:

com/acme/FooBase.as:

package com.acme {
import ext.Component;

public class FooBase extends Component {
  private var someConfig:String;

  public function FooBase(config:Foo = null) {
    this.someConfig = config.someConfig;
    super(config);
  }

  // use private field this.someConfig
}
}

com/acme/Foo.mxml:

<?xml version="1.0" encoding="UTF-8"?>
<local:FooBase xmlns:fx="http://ns.adobe.com/mxml/2009"
               xmlns:local="com.acme.*">
  <fx:Declarations>
    <fx:String id="someConfig"/>
  </fx:Declarations>
  ...
</local:FooBase>

This code pattern is a misunderstanding in the sense that the Component constructor already takes care of copying all config properties to the instance (this), so using this pattern, we end up with two properties someConfig, one private and one public one. Also, accessing the subclass and its properties in the superclass (base class) is not a good idea, as it leads to a cyclic dependency between the two classes.

So a slightly better pattern replaces the private field by a native accessor, which used to model Ext's semantics that config options are automatically copied to the component instance:

com/acme/FooBase.as:

package com.acme {
import ext.Component;

public class FooBase extends Component {
  public native function get someConfig():String;

  public function FooBase(config:Foo = null) {
    super(config);
  }

  // use public field this.someConfig
}
}

After migration to MXML, where the config class is merged into the component class, this leads to a duplicate declaration of someConfig and keeps the forward reference to the subclass.

Thus, if you used any of the above patterns, you should clean up your migrated code as follows.

If the config property is used by the base class at all, pull the config property declaration(s) from the MXML class up to the base class, replacing the private member or public native get method for that config property by the code taken from the MXML class. If there was a private member with a (slightly) different name than the config property, you should rename it to exactly match the config property name.

If the config property is actually not needed by the base class, simply remove the private member / accessor and the assignment code in the constructor.

Note that any property declared in an MXML <fx:Declarations> block is implicitly [Bindable], so when you convert this declaration into ActionScript, don't forget to add this annotation (see example below). If you are sure that the property is never changed from its initial configuration, you can alternatively use the annotation [ExtConfig], which keeps Ext from generating getSomeConfig() and setSomeConfig() methods, but still tags the field as an Ext config option.

Since now, all config options the base class needs to know about are declared right there, you can change the type of the constructor config variable to the base class, getting rid of the forward reference to the subclass.

The above example would be fixed like so:

com/acme/FooBase.as:

package com.acme {
import ext.Component;

public class FooBase extends Component {

  [Bindable]
  public var someConfig:String;

  public function FooBase(config:FooBase = null) {
    super(config);
  }

  // use public field this.someConfig
}
}

com/acme/Foo.mxml:

<?xml version="1.0" encoding="UTF-8"?>
<local:FooBase xmlns:fx="http://ns.adobe.com/mxml/2009"
               xmlns:local="com.acme.*">
  ...
</local:FooBase>

Resolve name clashes

Declaring a property as [Bindable] lets the Jangaroo compiler generate JavaScript code that in turn leads to Ext JS generating a getFoo() and setFoo() method (see MXML). The Jangaroo compiler also takes care of these methods being called when you access such properties through the normal dot syntax (this.foo, component.foo). However, the generated methods exist at run-time and may clash with methods that already existed before migration.

For example, if you had an EXML component with a config option style and getStyle() and setStyle() custom methods, after migration to MXML, these methods would now clash with the getStyle() / setStyle() methods generated by Ext JS.

Usually, your custom methods are no longer needed. If getStyle() just returns this.style and setStyle() just sets this.style, you implemented what is now done automatically and can now be accessed using simple property access syntax in ActionScript.

Type Casts

Type casts using the syntax SomeClassOrInterface(someValue) (as opposed to someValue as SomeClassOrInterface) used to be ignored at runtime. Now, such casts generate code to actually perform a type check at runtime and throw an error if the value does not match the type. This may cause (or rather: reveal!) problems in your code that seemed to work before, maybe because of "duck typing".

Note that due to the way Jangaroo 4 treats Ext JS config objects (see MXML), simple objects can be type-cast into any type. The type cast merely adds an xclass attribute to the object, so that Ext JS can instantiate the "real" object from that config object later.

New Action#tooltip Config in Ext AS 6

The Ext AS API ext.Action now defines a tooltip attribute and a setTooltip() function. Setting the tooltip already worked with Ext JS 3.4, but Jangaroo 2's Ext AS did not declare this config because it was not documented.

NodeInterface

While ext.data.NodeInterface is a class in Ext JS (and used to be one in Ext AS, too), it is now represented as an interface in the new ActionScript Ext AS API. Reason: It is not a real class, but only a duck-typed decorator for models. The runtime has been adapted so that tree nodes are recognized as implementing NodeInterface.

Clone this wiki locally