Ext JS is a mature framework. We’ve added a tremendous number of powerful new features and capabilities since the first release in 2007. Thousands of applications have been written using different versions of the framework. And some of these successful projects are still using Ext JS 3.4.
Some developers want to migrate their projects to the latest version so they can take advantage of new features, but they are unsure how to do so. Many of them give the same reasons for not migrating. They have a stable application, all bugs are fixed, and everything works properly because they chose one of the Best Javascript Libraries. At the same time, because the application is complex and contains numerous custom components and extensions, they believe the migration will be difficult.
On the other hand, almost all of them name the same advantages of upgrading to Ext JS 6:
- Don’t have to control all browser updates.
- Reduce time and effort of maintaining their application.
- Improve performance and memory usage.
- Take advantage of support for mobile devices and touch events.
- Access to new Ext JS features and functionality that can be added
with a couple of lines of code.
We always recommend that our customers migrate their applications to the most recent version of the framework – Ext JS 6.
In this article, I will show you a simple example of migration. As proof of concept, I have an Ext JS 3.4 grid example, and I migrated it to Ext JS 6. You can review the source code of the original and final applications.
We will build our new Ext JS 6 application based on the MVVM (Model-View-ViewModel) architectural pattern. MVVM provides an abstraction of a View (the ViewModel) that manages the changes between a Model‘s data and the View‘s representation of that data (i.e. data bindings). Read more about MVVM pattern.
Table of Contents
How can I Generate a new empty Ext JS 6 project?
We can generate an empty Ext JS 6 project using the command line tool Sencha Cmd. Just navigate to Ext JS 6 SDK folder with your command-line or terminal, and run the following command:
sencha generate app -classic -starter=false Migration ../migration
You can read more about Sencha Cmd features.
Initialize a new project
We need to add Application and Main View classes to our empty project. These classes are necessary to run our application.
-
- We can generate the Main view using Sencha Cmd as well. Go to a newly created migration folder and run this command:
sencha generate view main.Main
-
- We will generate the Application class manually. Create a file app/Application.js with this content:
Ext.define('Migration.Application', { extend: 'Ext.app.Application', name: 'Migration' });
-
- The last step is building our Ext JS 6 application and running it using an internal web server. Run the following commands from migration folder:
sencha app build sencha web start
- Navigate to http://localhost:1841/ with your browser to check that the application is running. You should see the following screen:
How can I Migrate the Model?
Now we can start the actual migration of our Ext JS 3.4 code into our new Ext JS 6 project. The first step here is to migrate the Model. The Model describes a format for the data being used in the application. In Ext JS 3.4, the data format should be defined inside of a store definition using the fields property.
var store = new Ext.data.ArrayStore({ fields: [ {name: 'company'}, {name: 'price', type: 'float'}, {name: 'change', type: 'float'}, {name: 'pctChange', type: 'float'}, {name: 'lastChange', type: 'date', dateFormat: 'n/j h:ia'} ] });
It’s a backward compatible approach, so it’s possible to define the data format in the same way in newer versions, including Ext JS 6. But the proper way to do it would be a definition of a separate Model class, where we can define fields and other logic related to the data.
- Create a Model class
-
- Create app/model/Company.js file with this content:
Ext.define('Migration.model.Company', { extend: 'Ext.data.Model' });
-
- Copy fields property from Ext JS 3.4 store to our model:
Ext.define('Migration.model.Company', { extend: 'Ext.data.Model', fields: [ {name: 'company'}, {name: 'price', type: 'float'}, {name: 'change', type: 'float'}, {name: 'pctChange', type: 'float'}, {name: 'lastChange', type: 'date', dateFormat: 'n/j h:ia'} ] });
The Store stays basically the same as in Ext JS 3.4. It is a collection of records (instances of a Model class).
-
- Create a Store class
-
- Create app/store/Company.js file with this content:
Ext.define('Migration.store.Companies', { extend: 'Ext.data.ArrayStore', alias: 'store.companies' });
-
- Our Store class should use our newly created Model:
Ext.define('Migration.store.Companies', { extend: 'Ext.data.ArrayStore', alias: 'store.companies', requires: ['Migration.model.Company'], model: 'Migration.model.Company' });
-
How can I Migrate the View?
The basic usage of Grid Panel stays the same as in Ext JS 3.4. The main difference is that now we need to define our Grid Panel in a separate View class.
-
- First we should create an empty template View class for our Grid Panel. Run the following command from the migration folder:
sencha generate view company.Grid
-
- Edit app/view/company/Grid.js file:
- Set base class to Ext.grid.Panel (not Ext.grid.GridPanel like it was in Ext JS 3.4):
extend: “Ext.grid.Panel” - Add xtype: ‘companygrid’ property, this is the reference to our Grid Panel that we will use later
- Remove html property
- Set base class to Ext.grid.Panel (not Ext.grid.GridPanel like it was in Ext JS 3.4):
- Copy the Ext JS 3.4 grid column definition and other properties of Grid Panel into new Grid View and comment out all functions for now (we will migrate them later).
- Remove autoExpandColumn: ‘company’ config and add flex: 1 config to the Company column. The flex properties are a new way to manage column width. You can read more about Ext.Component-cfg-flex.
- Change icon and getClass properties of action column to iconCls with references to FontAwesome icons.
- Edit app/view/company/Grid.js file:
When you finish, your code should look similar to this:
Ext.define("Migration.view.company.Grid",{ extend: "Ext.grid.Panel", requires: [ "Migration.view.company.GridController", "Migration.view.company.GridModel" ], controller: "company-grid", viewModel: { type: "company-grid" }, columns: [ { id :'company', header : 'Company', width : 160, sortable : true, dataIndex: 'company', flex: 1 }, { header : 'Price', width : 75, sortable : true, //renderer : 'usMoney', dataIndex: 'price' }, { header : 'Change', width : 75, sortable : true, //renderer : change, dataIndex: 'change' }, { header : '% Change', width : 75, sortable : true, //renderer : pctChange, dataIndex: 'pctChange' }, { header : 'Last Updated', width : 85, sortable : true, //renderer : Ext.util.Format.dateRenderer('m/d/Y'), dataIndex: 'lastChange' }, { xtype: 'actioncolumn', width: 50, items: [{ iconCls: 'x-fa fa-minus', tooltip: 'Sell stock'/*, handler: function(grid, rowIndex, colIndex) { var rec = store.getAt(rowIndex); alert("Sell " + rec.get('company')); }*/ }, { iconCls: 'x-fa fa-plus', /* handler: function(grid, rowIndex, colIndex) { var rec = store.getAt(rowIndex); alert("Buy " + rec.get('company')); }*/ }] } ], stripeRows: true, height: 350, width: 600, title: 'Array Grid', stateful: true, stateId: 'grid' });
-
- Now we need to add our Grid to the Main View. Our Main View is an instance of Ext.panel.Panel, and we just need to add our Grid to Main View‘s items array (using previously defined xtype) and requires array (using class name).
Ext.define("Migration.view.main.Main",{ extend: "Ext.panel.Panel", requires: [ "Migration.view.main.MainController", "Migration.view.main.MainModel", "Migration.view.company.Grid" ], controller: "main-main", viewModel: { type: "main-main" }, items: [{ xtype: 'companygrid' }] });
-
- Run the following commands from migration folder:
sencha app build sencha web start
- Navigate to http://localhost:1841/ with your browser to check that the application is working. You should see the following screen:
How do I Create a ViewModel?
The ViewModel is a new abstraction that was released in Ext JS 5. It manages changes between the View and an associated Model. When we generated our Grid View using Sencha Cmd, the ViewModel class was generated as well.
-
- Edit ViewModel file app/view/main/GridModel.js:
- Remove data property from the ViewModel class
- Add companies store to the stores config of the ViewModel class
- Assign data config of companies store to the data from Ext JS 3.4 code
- Edit ViewModel file app/view/main/GridModel.js:
Ext.define('Migration.view.company.GridModel', { extend: 'Ext.app.ViewModel', alias: 'viewmodel.company-grid', requires: [ "Migration.store.Companies" ], stores: { companies : { type: 'companies', data: [ ['3m Co', 71.72, 0.02, 0.03, '9/1 12:00am'], ['Alcoa Inc', 29.01, 0.42, 1.47, '9/1 12:00am'], … ] } } });
-
- Bind our Grid to the ViewModel
Now we need to add bind config to our Grid View to bind our grid to companies store from ViewModel:
bind: { store: '{companies}' },
Am I able to Migrate Code into ViewController?
The ViewController is a part of MVVM pattern as well. Individual ViewControllers are created for each related View. They listen for events and execute application business logic. In our case, we will move the commented-out function to ViewController. It will contain handlers for add and delete buttons of actioncolumn and column renderers. ViewController for our Grid View has also been generated by Sencha Cmd.
-
- Move change function from Ext JS 3.4 example into GridController.js file and rename it to columnRenderer.
- Move pctChange function from Ext JS 3.4 example into GridController.js file and rename it to pctColumnRenderer.
- Move handler function of actioncolumn delete button to GridController.js and rename it to sellStock. Change reference to the store in the code to grid.getStore().
- Move handler function of actioncolumn add button to GridController.js and rename it to buyStock. Change Reference to the store in the code to grid.getStore().
When you finish, your GridController.js file should look similar to the following code:
Ext.define('Migration.view.company.GridController', { extend: 'Ext.app.ViewController', alias: 'controller.company-grid', columnRenderer : function(val) { if (val > 0) { return '' + val + ''; } else if (val < 0) { return '' + val + ''; } return val; }, pctColumnRenderer : function (val) { if (val > 0) { return '' + val + '%'; } else if (val < 0) { return '' + val + '%'; } return val; }, sellStock: function(grid, rowIndex, colIndex) { var rec = grid.getStore().getAt(rowIndex); alert("Sell " + rec.get('company')); }, buyStock: function(grid, rowIndex, colIndex) { var rec = grid.getStore().getAt(rowIndex); alert("Buy " + rec.get('company')); } });
-
- Add references to this functions inside Grid View and replace renderer : ‘usMoney’ to renderer : Ext.util.Format.usMoney:
Ext.define("Migration.view.company.Grid",{ extend: "Ext.grid.Panel", requires: [ "Migration.view.company.GridController", "Migration.view.company.GridModel" ], xtype: 'companygrid', controller: "company-grid", viewModel: { type: "company-grid" }, bind: { store: '{companies}' }, columns: [ { id :'company', header : 'Company', flex : 1, sortable : true, dataIndex: 'company' }, { header : 'Price', width : 75, sortable : true, renderer : Ext.util.Format.usMoney, dataIndex: 'price' }, { header : 'Change', width : 75, sortable : true, renderer : 'columnRenderer', dataIndex: 'change' }, { header : '% Change', width : 75, sortable : true, renderer : 'pctColumnRenderer', dataIndex: 'pctChange' }, { header : 'Last Updated', width : 85, sortable : true, renderer : Ext.util.Format.dateRenderer('m/d/Y'), dataIndex: 'lastChange' }, { xtype: 'actioncolumn', width: 50, items: [{ iconCls: 'x-fa fa-minus', tooltip: 'Sell stock', handler: 'sellStock' }, { iconCls: 'x-fa fa-plus', handler: 'buyStock' } ] } ], stripeRows: true, height: 350, width: 600, title: 'Array Grid', stateful: true, stateId: 'grid' });
-
- Navigate to http://localhost:1841/ with your browser to check that the application is working. You should see the following screen:
Are you ready to get started migrating?
This guide is just a sample of how to migrate your application from Ext JS 3.4 to Ext JS 6. You can find more information in the upgrade guides:
Ольга, писаните пожалуйста мне этот текст в Русском варианте :)
Привет, zeus! К сожалению, у меня нет русского варианта этого текста.
Hi, zeus! Unfortunately I don’t have a russian version of this article.
Ольга, здравствуйте! Спасибо за статью!
Подскажите, пожалуйста, взаимодействие между GridViewController и MainViewController должно происходить только через event’ы? что делать, если для gridview и mainview мне нужна одна viewmodel? Можно ли иметь 2 view с одной viewmodel и разными контроллерами? (вариант с наследованием viewmodel работает, но насколько это правильное решение) И как правильно избавиться от проблемы, когда один контроллер становится очень громоздким? Разделять его с помощью mixins или как-то ещё? Спасибо!
Привет, Сергей!
GridView может не иметь собственной модели и использовать модель своего родителя – MainView. Это никак не отражается на контролерах – MainView и GridView могут иметь отдельные контроллеры. Наследовать модели в этом случае не нужно.
GridViewController и MainViewController могут взаимодействовать друг с другом тоже через MainViewModel. Например, если что-то просходит в GridView, GridViewController может изменить данные в MainViewModel. И эти данные могут использоваться MainView или MainViewController.
Насчет решения проблемы громоздкого контроллера – я бы предложила решить ее через разделение View. Если у вас громоздкий контролер, то скорее всего View тоже содержит несколько компонентов. Вы можете вынести некоторые подкомпоненты в отдельный View и перенести всю логику, связанную с вынесенными подкомпонентами в отдельный контроллер. Также можно какую-то логику, не связанную напрямую с UI компонентами, перенести в сервисный singleton.
—–
Hi Sergey!
GridView may not have a separate ViewModel and use the ViewModel of his parent – MainView. Inheritance is not needed in this case. At the same time MainView and GridView could have separate ViewControllers.
GridViewController and MainViewController could communicate via MainViewModel as well. When user interacts with GridView – GridViewController changes data in MainViewModel and these data are used by MainView and MainViewController.
You could split a huge ViewController by splitting his View – just move some components(s) from the View into a separate View and move all logic related to this component(s) into his separate ViewController.
Or you may move some logic that is not related to UI components into a service singleton.
Comments in English please. Nice article by the way.
Thank you Johan!
Great work. Unfortunately for people who use SA, we are still in a fix. I regret using SA.
Hi!
Thanks for your comments. We are working on new version of Architect and I will like to know your specific needs. I will send you email for clarification. Thanks!
Hello Sandeep, if you really ask about specs for SA. How about websocket integration.
I wished they used Sencha CMD to generate the MetaData used by SA to create an Architect project. This way I can hand off parts of the project to green dev’s and they can get upto speed super fast!
Thanks for your comments. We are working on roadmap of Cmd and Architect and I will send you email to understand your specific request. Thanks!
Nice Article.Looking forward for more articles from you.
Hi!
Olga thx for the Post, but yuor example is pretty simple.
I our application we have about 2000 ExtJS classes with stores, records, panel etc.
And rewrite all classes to new engine is very expensive operation.
Maybe you have adapter to run extjs3 code in extjs6 framework version?
Hi Dmitriy,
Thank you for your comment!
I have replied you directly via email.
Best,
Olga
Hi Olga,
We are recently upgraded ColdFusion 9 to ColdFusion 11 and we are using cfgrid for building grid. In CF9 the extjs version 3.1.0 and in CF 11 extjs version 4.1.
I need the equivalent methods in extjs 4.1 for the following
grid.getColumnModel().config[n].menuDisabled = true;
grid.getColumnModel().config[n].sortable = false;
grid.getColumnModel().setRenderer(n, Ext.util.Format.dateRenderer(‘m/d/Y’));
grid.getBottomToolbar().destroy();
grid.reconfigure(grid.getStore(), grid.getColumnModel());
grid.addListener(“rowclick”, somefunction);
grid.addListener(“rowdblclick”, somefunction);
grid.getStore().addListener(‘load’, somefunction);
grid.selModel.getSelected().data;
Regards,
Bala.P
Hi Bala! I’ll get in touch with you directly.
Yes I agree with the other comment, this is a simple bordering on hello world example. Every app we’ve upgraded generally takes a couple of man months of work to convert and we experience fall out for months afterward with lots of little things which are different. Even point release changes like going from 6.0.2 to 6.2EA are painful. Same for other point release like 4.2.1 to 4.2.2. Unfortunately many times the only answer we have to get something fixed is ‘you should upgrade to version x.y.z’ which many times can’t be justified to management.
Hi Miro,
thank you for your comment!
I’ll contact you via email.
Can I use ExtJS 6.2 EA for application development? Once the ExtJS 6.2 released, Can I migrate from ExtJS6.2 EA to ExtJS 6.2?
Hi Venkat,
You can use Ext JS 6.2 EA to test small apps, but it is not recommended for production apps. Ext JS 6.2 will be released soon. And you could easily migrate from ExtJS6.2 EA to ExtJS 6.2.
Keep an eye on the blog for details.
Thanks for the reply Olga Petrova. We are started migrating from ExtJs 4.2.2 to Extjs6.2 EA. But we will migrate and release the application once the ExtJS 6.2 released. Looking forward to the release.
Hi Olga,
We are recently upgraded ColdFusion 9 to ColdFusion 11 and we are using cfgrid for building grid. In CF9 the extjs version 3.1.0 and in CF 11 extjs version 4.1.
I need the equivalent methods in extjs 4.1 for the following
grid.getColumnModel().config[n].menuDisabled = true;
grid.getColumnModel().config[n].sortable = false;
grid.getColumnModel().setRenderer(n, Ext.util.Format.dateRenderer(‘m/d/Y’));
grid.getBottomToolbar().destroy();
grid.reconfigure(grid.getStore(), grid.getColumnModel());
grid.addListener(“rowclick”, somefunction);
grid.addListener(“rowdblclick”, somefunction);
grid.getStore().addListener(‘load’, somefunction);
grid.selModel.getSelected().data;
Regards,
Bala.P
Nice post to help with the migration. Your example is very basic. We have multiple applications with a huge number of files. It would take a while to rewrite these applications to the model in the example you provided. Is there an easier way to do this migration? Can we run an older version of Ext JS say 3.0 code in Ext JS 6.2 framework? Any other options other than painfully rewriting all the applications?
Hi Priya, our professional services team has helped many customers with upgrading their applications. We’d be happy to discuss your case!
Hi Olga, I’m interested in the same question. Is it possible to run an 4.2 version of Ext JS code in Ext JS 6.2 framework?
Hi Olga! Our Professional Services team can help you find an appropriate option in your particular use case. Just fill out the Contact Us form – https://www.sencha.com/company/contact/
Hi Olga,
thanks for your example.
I made all the steps starting from the installation of the cmd and the extjs until before “Create a ViewModel”, but all I see is the default “Hello Ext JS’, ‘Hello! Welcome to Ext JS”, instead of the grid.
The content of the app.js from the migration folder is displayed.
I can’t see any mistakes on my side.
Do you have an idea, what might have gone wrong?
Thanks in advance for your answer!
Regards,
Kalleinz
Hi Kalleinz!
I have replied you via email.
Best wishes,
Olga
Hiiii guys, same issue here. May it is possible to answer directly here or as well by mail? Thaanks!
Why you call it ‘migration’? you wrote Grid from scratch )))
I have re-used the most part of the Model. In the View I have re-used the column definitions and I have moved existing column renderers and handlers (almost without modification) to the ViewComproller. So I would say at least 50% of the code is re-used.
Thanks for the article, it’s helpful. I have a question:
How to integrate extjs6 modern app in extjs3.4 app structure? We have a desktop application that is built with extjs3.4 and we want to build a modern (mobile + tablet) app for it in extjs6. Most of the extjs3.4 code was updated to 6 but still the same app structure of the extjs3.4. So how to add the modern app folder into the extjs3.4 structure and needs to be modified to make it work?
Best regards,
Joud
Hello,
Would this article be applicable to upgrade to ExtJS 6 from 4.1? Are there any considerable changes between the approach from 3 vs approach from 4?
Thank you,
Marian
Hi Marian,
Not exactly. Ext JS 4 applications follow MVC pattern and Ext JS 3 applications do not follow any architectural pattern (by default).
There is post related to migration from Ext JS 4 / Sencha Touch 2 to Ext JS 6: https://www.sencha.com/blog/how-to-upgrade-a-sencha-touch-app-to-ext-js-6-modern-toolkit-part-1/
What would you recommend in order to upgrade a Java Web App which is using ExtJS 3? Can we use ExtJS 6 or 7? Thanks in advance.
I would recommend to migrate directly to Ext JS 7. The effort should be relatively equal. This post was written before Ext JS 7 was released.