Mastodon

Migrating from ExtJS 3 to ExtJS 4

If a project lives long enough, it can be a good idea to update one part or the other. That way, new features can be used and the code base is up to date. During the last weeks I updated the frontend of a larger web application from ExtJS 3 to ExtJS 4. A lot of custom CSS rules were used. The Migration Guide by Sencha covers the migration for smaller applications but it is not sufficient for our large application. This article documents the migration process, showing the difficulties and the decisions made. It is not supposed to be a complete How-to-guide.

Step 1 - Creating an Ext 4 Workspace within the Ext 3 Application

There are three general approaches for an ExtJS migration:

  1. Re-Implement your whole application in ExtJS 4.
  2. Sandboxing - Sencha provides a compatibility layer which allows the use of ExtJS 4 components within an ExtJS 3 application.
  3. Adding support for native ExtJS 4 and migrating page by page.

Option number 1 would have been way to expensive. Option number 2 allows for step-by-step-migration, but the compatibility layer should only be used for a short time during the migration. Also, the use of the layer would have polluted our namespace with Ext3.x - calls, so we dismissed that option. Adding support for native ExtJS 4 would allow us to migrate page by page, use “real” ExtJS 4 and being able to update it at any time. The decision went in favor for option 3.

The application uses Tiles, a templating framework for websites. It allows to define regions (“tiles”), for a website. The typical header-navigation-content style of a website could be modeled with Tiles by defining these three tiles. Because different pages of one website can have different layouts, Tiles allows a mapping from an URL to a specific page layout. In that way, all “/tutorial”- pages of a programming-website could have no navigation menu whereas all other pages have one. In this way, Tiles can be used for the separation of the ExtJS 3 from the ExtJS 4 pages. To achieve that, I configured all new ExtJS 4 pages to use a special JSP which allows the use of ExtJS 4.

tiles-def.xml holds one tile-definition for ExtJS 3 (the default for the application) and one for the new ExtJS 4:

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

<!DOCTYPE tiles-definitions PUBLIC
    "-//Apache Software Foundation//DTD Tiles Configuration 2.1//EN"
    "http://tiles.apache.org/dtds/tiles-config_2_1.dtd">

<tiles-definitions>

  <definition name="default" template="/WEB-INF/layout/classic.jsp">
    <put-attribute name="title" value="MyApp"/>
    <put-attribute name="header" value="/WEB-INF/layout/header.jsp"/>
    <put-attribute name="content" value="/WEB-INF/layout/blank.jsp"/>
  </definition>

  <definition name="extjs4" template="/WEB-INF/layout/extjs4.jsp">
    <put-attribute name="title" value="MyApp-ExtJS4"/>
    <put-attribute name="header" value="/WEB-INF/layout/header.jsp"/>
    <put-attribute name="content" value="/WEB-INF/layout/blank.jsp"/>
  </definition>

</tiles-definitions>

tiles-pattern.xml decides with patterns which tiles-definition shall be used. The following pattern resolves every page under /app/pages/extjs4test with ExtJS 4.

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

<tiles-pattern>

  <!-- Default-Template -->
  <default template="default" content="content" title="MyApp"/>

  <!--!!! All pages that shall be rendered in ExtJS 4 go here !!!-->
  <pattern name="/app/pages/extjs4test/*" template="extjs4" content="content" title="MyApp-extjs4"/>
  <pattern name="/app/pages/extjs4test" template="extjs4" content="content" title="MyApp-extjs4"/>
</tiles-pattern>

The new JSP for all ExtJS 4 pages only supports ExtJS 4:

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib uri="http://tiles.apache.org/tags-tiles" prefix="tiles" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ taglib uri="http://java.sun.com/jstl/fmt" prefix="fmt" %>
<%@ taglib uri="http://www.springframework.org/tags" prefix="spring" %>
<%@ taglib uri="http://packtag.sf.net" prefix="pack" %>

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Strict//EN">

<html>
<head>
  <title><tiles:getAsString name="title" ignore="true"/></title>
  <meta http-equiv="content-type" content="text/html;charset=UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=8"/>

<pack:style src="/resources/css/my-ext-theme.css"/>
<pack:script src="/js/prototype/prototype-1.6.1.js"/>

  <script type="text/javascript" src="<c:url value="/js/ext-4.0.7/ext-all-debug.js"/>"></script>

  <script type="text/javascript">
    Ext.BLANK_IMAGE_URL = '<c:url value="/js/ext-4.0.7/resources/themes/images/default/tree/s.gif"/>';
  </script>

  <script type="text/javascript" src="<c:url value="/js/app/ownJavaScriptClasses.js"/>"></script>
  <script type="text/javascript" src="<c:url value="/js/ext-4.0.7/locale/ext-lang-${locale}.js"/>"></script>

</head>
<body id="body">

  <div id="JSError" style="position:absolute;top:-100px"></div>

  <script type="text/javascript">
  window.onerror = function(msg) {
    $('JSError').addClassName(msg + ';');
  }
  </script>

  <div id="content">
    <tiles:insertAttribute name="content"> </tiles:insertAttribute>
  </div>

</body>
</html>

This approach makes it possible to write Ext 4 code into the existing Ext 3 framework, which saves the time needed for a complete reimplementation. On the other hand there are two versions of ExtJS in the project which have to be supported. By doing this, I risk having to implement all your components in two different versions. The goal by choosing this approach should be to move completely to ExtJS 4 as quickly as possible.

Step 2 - Migrating Components

If the application consisted only of original ExtJS components, it would have been no problem to go on with the normal development at this point. However, the application uses a lot of specialized classes which are derived from ExtJS components, override them or are own self written components. One of them is the viewport. Basically, this is a container for other ExtJS components with some special CSS styles and behavior. It also defines a visible layer and a menu. After having set up ExtJS 4, the next goal should be to show an empty Ext 4 - page using the viewport.

Changes in the existing code of the custom components are necessary, because the Upgrade to ExtJS 4 brought some new syntax. For example, a new class system was introduced in the new version. I had to manually rewrite all Ext.reg() and Ext.extend() to the new Ext.define(), which was relatively easy. Before rewriting that code, a lot of errors occurred. Same busy work has to be done with Function.createDelegate() which now is Ext.Function.bind() and Function. createCallback() which now is Ext.Function.pass(). Very useful is the overview in these slides. Also, the form layout is not included in ExtJS 4 anymore, so another layout has to be used and manually assigned.

Facing these refactorings, I decided for the following refactoring steps:

  1. Most of the own components are defined in seperate Java Script files. I copied one file after another into the new ExtJS 4 codebase.
  2. After copying a JS-file into the new codebase, I comment all the code.
  3. Then, I un-comment only code that works.
  4. Encountering another customized component, I proceeded with step 1. With the above algorithm, I often ran into “namespace is undefined” errors because of own commented components which could not be found. This message is not very useful and can only be eliminated by commenting out small chunks of code, often just single lines.

During the process, a lot of small changes in the new version caused minor problems. For example, in ExtJS 3 a container can have a layout-object with information regarding the components inside this container. Also, the container can be layouted by giving a string with the layout name instead of an object. The problem is: ExtJS 4 only knows the layout-object, so the layout-string will cause an error. This is not mentioned in the migration guide, I found it in the sencha forum.

In the legacy-code I also encountered some problems which could be solved by allways defining a layout for all containers. ExtJS 3 seemed to be more unconcerned with missing layouts.

The stores luckily did not change much. For example, there is an I18nStore for translating texts in the GUI, which is extended from Ext.data.JsonStore. This ExtJS 3 version

{
   storeId: 'i18nStore',
   root: 'data',
   listeners: {
      load: this.loadComplete
   },
   proxy: new Ext.data.HttpProxy({url: this.config.i18nUrl}),
   baseParams : baseParams,
   fields: [
      'name','text'
   ]
}

turned into this code for ExtJS 4:

{
   storeId: 'i18nStore',
   listeners: {
      load: this.loadComplete
   },
   proxy: {
      type: 'ajax',
      url: this.config.i18nUrl,
      reader: {
      type: 'json',
      root: 'data',
      idProperty: 'name'
      },
      extraParams : baseParams
   },
   fields: [
      'name','text'
   ]
}

After three days I managed to render an empty page with an ExtJS 4 version of the viewport.

Step 3 - Theming

After being able to show an ExtJS 4 page at all, the next step was to style it so it looked like the old version. The application uses two own CSS files which overwrite parts of the ext-all.css by being included in the JSP right after the ext-all.css. The custom CSS files are huge and not easy to read, as there are a lot of CSS rules that build on top of each other and specifying styles for a lot of components. After upgrading ExtJS, parts of the application looked odd because the new CSS classes didn`t match the old ones.

ExtJS 4 provides a new way of styling a web application: Theming. A very good tutorial is the guide to custom themes in ExtJS 4. Following it, I immediately ran into the error message “mixins/_frame.scss:115: Defining a function within a mixin is not allowed.” when compiling SASS themes (see here). I solved it with downgrading SASS to 3.1.1 like it was mentioned in the sencha forums. After some days of theming and finding the right variables for what I wanted to change, I managed to display a full ExtJS 4 rendered page with a similar look like the ExtJS 3 pages of the application. The used SASS-file is much smaller thanks to using Theming but also includes some small CSS rules for special styles.

Determing which DOM element is styled by which CSS rule can be quite exhausting without the proper tools. I used Firebug and Illumination. Firebug is a plugin for Firefox which provides usefull functions like on-the-fly-editing of CSS and, most important, great JS debugging. Don`t even start without it. Illuminations is a plugin for firebug which helps you to identify ExtJS components.

Outlook & Summary

After this first phase of migrating, next steps will be perfectioning the theming and migrating all of the components. This could take some time so it would be best to perform this refactoring together with implementing new features. This way, the busy work does not have to be done in one (boring) step and the components can be fine tuned to the new features.

At first, the amount of changes scared me to begin with the migration. Thanks to the help from Sencha and the amount of forum posts and other guides I quickly made progress. In conclusion, migrating from ExtJS 3 to ExtJS 4 is a huge, but manageable task.