Blog de Antonio Manuel Muñiz

Desarrollo, Ingeniería y Calidad del Software

Migración de ExtJS 3 a ExtJS 4

En klicap estamos llevando a cabo la migración de uno de nuestros productos (Opina 2, aún en desarrollo) a ExtJS 4. He creido oportuno compartir aquí algunas notas sobre la migración.

Uso de Ext.define y Ext.create

En ExtJS 4 las clases se definen con Ext.define y se crean instancias con Ext.create, lo cual sustituye a Ext.extend y new en ExtJS 3

ExtJS 3:

Ext.namespace('Opina.view');
Opina.view.TextQuestion = Ext.extend(Ext.grid.GridPanel, {
    initComponent: function() {
        ...
        Opina.view.TextQuestion.superclass.initComponent.call(this);
    }
});
Ext.reg('textquestion', Opina.view.TextQuestion);

ExtJS 4:

Ext.define('Opina.view.TextQuestion', {
    extend: 'Ext.grid.Panel',
    alias: 'widget.textquestion',
    initComponent : function() {
        ....
        this.callParent(arguments);
    }
});

Este fragmento de código está lleno de detalles:

  • Uso de Ext.define y el parámetro extend
  • Nueva nomenclatura, Ext.grid.GridPanel pasa a ser Ext.grid.Panel (regla extensible a todo el framework)
  • Nueva función callParent presente en todos los componentes
  • Definición automática de xtype usando alias

2. Uso de Ext.data.Model y Ext.data.Store
En ExtJS 4 se ha separado por completo el concepto de almacén de datos (Store) y definición de estructura de datos (Model). Lo que era un Store con una serie de campos (fields) ahora es un Store asociado a un modelo:

Ext.define('Opina.model.TextQuestion', {
    extend: 'Ext.data.Model',
    fields: [
       { name: 'elementSetId' },
       { name: 'elementType' },
       { name: 'freeTextLabel' },
       { name: 'helpMessage' },
       { name: 'id' },
       { name: 'position' },
       { name: 'questionType' },
       { name: 'surveyId' },
       { name: 'template' },
       { name: 'wording' },
       { name: 'multiplelines', mapping: 'data.multipleLines' },
       { name: 'validations' }
    ]
});
var store = Ext.create('Ext.data.Store', {
    model: 'Opina.model.TextQuestion',
    proxy: {
        type: 'ajax',
        reader: {
            type: 'json',
            root: 'question'
        }
    }
});

3. Mejora de formularios

El layout «form» ha desaparecido. En ExtJS 3 si querías un campo de texto con un label (muy común) la única opción era que el campo estuviera dentro de un panel con layout «form». En ExtJS 4 cualquier layout puede acojer un campo con label, lo cual flexibiliza mucho la definición de formularios y evita el uso de paneles sin sentido que sólo contienen un campo.

4. Métodos up y down

ExtJS 4 proporciona dos métodos que facilitan la recuperación de elementos en un contenedor, sus hijos y ancestros. En ExtJS 3 se suele usar «find», pero down es más versátil al permitir el uso de expresiones de búsqueda complejas, por ejemplo «#» para buscar por «itemId».

var store = myWindow.down('#options-grid').getStore()

El funcionamiento de «up» es igual pero aplica a ancestros. Realmente útil.

5. Mejoras en Grids

Los grids han sufrido cambios importantes, sobre todo en lo relacionado a eventos. En ExtJS 4 los grids son un DataView más, por tanto cada fila es un item y se trata como tal. No existen los eventos del tipo «rowdblclick» sino «itemdblclick».

6. Organización

Esto no está directamente relacionado con la migración, pero el nuevo enfoque MVC de ExtJS hace que de forma natural tu proyecto esté mejor organizado. Nosotros hemos organizado el proyecto así:

7. Firefox + Firebug son tus amigos

Durante el proceso de migración ha sido fundamental el uso del binomio Firefox + Firebug. Sin estas herramientas la migración es un infierno.
Sencha proporciona un adaptador especial para la migración, que relaiza una traslación de ExtJS 3 a ExtJS 4 de ciertas propiedades, nombre de clases, paquetes y métodos. De esta forma podemos ir realizando la migración gradualmente y solucionando las incompatibilidades que son logeadas en la consola de Firebug.

8. Otros detalles
Dejo en el tintero muchos detalles de menor importancia, como cambios de nombre en métodos (que no son detectados por el pack de migración) como «reload()» en Ext.grid.Panel, que deja de existir estando disponible sólo «load», o el objeto resultado de un submit cuya respuesta viene en «action.result.data» y no en «action.reader.jsonData».

9. Sigo echándolo en falta
Sigo echando el falta un mecanismo que controle la carga asíncrona de un ComboBox, que tantos problemas da cuando la conexión es lenta (se hace el setValue antes de que los valores del combo hayan sido cargados). Paro solventar esto en @klicap hemos desarrollado una pequeña extensión, pero eso lo contaré en otro post ;-)

9 Respuestas a “Migración de ExtJS 3 a ExtJS 4

  1. Carlos R 12 noviembre 2011 en 11:10 pm

    ¿Qué extensión habéis desarrollado para la carga asíncrona de los combos?. Llevo tiempo peleándome con ExtJS 4 y los combos y no consigo llegar a una solución.
    Especialmente cuando se trata de combos paginados.

  2. Antonio Manuel Muñiz Martín 14 noviembre 2011 en 12:21 pm

    Hola Carlos,

    Hemos encontrado solución para los combos sin paginación. Es tan simple como retrasar la ejecución de setValue hasta que el combo se ha cargado, en unos días pongo por aquí el código fuente, es más simple de lo que puede parecer.

    Intento evitar el uso de combos paginados, son realmente problemáticos.

    Un saludo.

  3. Jo´se 3 diciembre 2011 en 2:41 am

    El tema de los combos los realizo de la siguiente forma:

    1ro. cargo el combo y en el callback del combo cargo el formulario. Así no tengo problemas de que el dato es erroneo.

    • Antonio Manuel Muñiz Martín 6 diciembre 2011 en 1:44 pm

      Hola José,

      Esa es la opción «inmediata», pero tiene el inconveniente de que en cada load de formularios tienes que gestionar el callback del combo. Mi propuesta es usar un combo «extendido» de forma que la carga se realice correctamente sin tener que controlar el load del formulario.

      En unos días publicaré un post sobre esto, lo tengo en el horno :-)

      Saludos.

      • Carlos R. 4 enero 2012 en 12:22 pm

        Si no se usan combos paginados y la cantidad de datos es muy grande, por ejemplo, combo de selección de cliente en una aplicación que gestiona 10000 clientes, tarda mucho tiempo en hacer la carga de datos. ¿Hay alguna solución para ello?

      • Antonio Manuel Muñiz Martín 4 enero 2012 en 7:47 pm

        Hola Carlos,

        No me he enfrentado a un combo lo sufientemente grande, de hecho para esa cantidad de datos hay otros componentes más adecuados. Pero si el combo es tu única opción, puedes realizar la petición de carga del combo indicando el identificador del elemento que deseas cargar, y que sea el servicio (del lado del servidor) el que te devuelva la página (y sólo la página) que contiene al elemento. Es una solución algo rebuscada y difícil de implementar.

        Yo optaría por revisar la UI y usar algún componente preparado para grandes cantidades de datos junto con alguna solución de selección personalizada, como un campo estático (o panel si la entidad es compleja) que haga binding con el grid (elemento seleccionado).

        Un saludo.

      • Jorge Luis Gomez 16 julio 2013 en 4:14 am

        Usando los has one y belongs to solucione este problema que fue una gran pesadilla en extjs 3 un par de lineas en el renderer

        var datos = Ext.data.StoreManager.lookup(‘MservicioStore’);
        if(datos.getById(value)!== null){
        return datos.getById(value).get(‘nombre’);
        }
        datos.add(record.getMservicio().data);
        return record.getMservicio().get(‘nombre’);

        y un beforequery:

        var currentSelected = queryPlan.combo.up(‘grid’).getSelectionModel().getSelection()[0].data.mservicio_id;
        queryPlan.combo.store.getProxy().extraParams = {
        current: currentSelected
        };
        return true;

        y listo!! puedo manejar miles de registros en el combobox.

        Usando Extjs MVC 4.2

  4. Daney 14 marzo 2012 en 5:48 pm

    Hola, si no esta la carpeta adapter la cual contenia ext-base.debug.js y ext-base.js cual js debo invocar.

Deja un comentario