Blog de Antonio Manuel Muñiz

Desarrollo, Ingeniería y Calidad del Software

ComboBox en Sencha ExtJS 4. Carga asíncrona

La clase  Ext.form.field.ComboBox en ExtJS es realmente potente, permitiendo desde paginación hasta la posibilidad de definir mediante Ext.XTemplate el contenido de cada línea del combo. Pero su comportamiento asíncrono puede convertirse en un problema cuando la conexión con el lado del servidor es lenta. En klicap usamos ExtJS desde hace casi dos años, y nos hemos encontrado con este comportamiento recurrentemente, ya era hora de solucionarlo :-)

Para situarnos. Todo componente de ExtJS que actúa como contenedor de datos tiene asociado un Store (Ext.data.Store) el cual define una serie de parámetros que afectan a la relación entre el contenedor y los datos:

  • Estructura de datos. Haciendo uso de Ext.data.Model (se establece una relación entre la fuente de datos y los objetos javascript que los mapean).
  • Características de la fuente. La fuente de datos puede encontrarse en memoria (un array que contiene los datos) o puede ser remota (lo cual implica el uso de AJAX). En este último caso se ha de definir el formato de los datos recibidos (JSON o XML).
  • Comportamiento. Se indica si el Store se cargará automáticamente al crear el objeto, o requiere una llamada a load().

Un ejemplo de definición de un combo (de timezone) con fuente de datos remota, JSON y carga automática (el combo se encuentra dentro de un formulario y este asu vez dentro de una ventana):

Ext.define('User', {
    extend: 'Ext.window.Window',
    initComponent: function() {
        this.items = [{
            xtype: 'form',
            border: false,
            fieldDefaults: {
                labelWidth: 100
            },
            items: [{
                xtype: 'fieldset',
                anchor: '100%',
                title: 'User data',
                items: [{
                    layout: 'anchor',
                    items: [{
                        xtype: 'combo',
                        fieldLabel: 'Timezone',
                        name: 'timezone',
                        width: 300,
                        queryMode: 'local',
                        triggerAction: 'all',
                        valueField: 'id',
                        displayField: 'timezone'
                        store: Ext.create('Ext.data.Store', {
                            model: 'Timezone',
                            proxy: {
                                type: 'ajax',
                                url: 'api/timezone',
                                reader: {
                                    type: 'json',
                                    root: 'timezones'
                                }
                            },
                            autoLoad: true
                        })
                    }]
                }]
            }]
        }];
        this.callParent(arguments);
    },
    modal: true,
    width: 430,
    height: 350,
    layout: 'fit',
    title: 'User'
});

Durante el proceso de carga de datos en el formulario ExtJS hace el mapping de los valores que vienen en el JSON en los campos del formulario (usando la propiedad name del campo). Por tanto la carga del formulario sería tan simple como:

var userForm = Ext.create('User');
userForm.getComponent(0).getForm().load({
    url: 'api/user/12',
    method: 'GET',
    success: function (form, action) {
        userForm.show();
    }
});

En este fragmento de código se realizarán dos peticiones HTTP GET al servidor: una para la carga de combo (GET ‘/api/timezone’ debido a la propiedad autoload: true del combo) y otra para la carga de los datos del usuario (GET ‘/api/user/12’).

Como he comentado antes ExtJS realiza llamadas internas a Ext.form.field.Base.setValue() para setear el valor de cada campo del formulario (como parte de la llamada a load). Pero ¿qué sucede cuando se intenta setear el valor del combo antes de que el listado de valores haya sido cargado?, es decir, la llamada a ‘/api/user/12’ termina antes que ‘/api/timezone’. El resultado es que el valor no se fija y el combo se queda vacío, y eso es un #FAIL.

Para evitarlo es necesario esperar a que la carga del listado de valores de combo haya terminado y sólo despues hacer el setValue(). La siguiente extensión se encarga precisamente de esto de una forma bastante simple:

Ext.define('AsyncSafeComboBox', {
    extend: 'Ext.form.field.ComboBox',
    alias: 'widget.safecombo',
    setValue: function(value, doSelect) {
        if(this.store.loading){
            this.store.on('load', Ext.bind(this.setValue, this, arguments));
            return;
        }
        this.callParent(arguments);
    }
});

La clave está en la propiedad ‘loading’ de Ext.data.Store que indica si el store asociado al combo está aún cargando sus datos. Si la llamada a setValue se realiza durante la carga del store, entonces se retrasa hasta que el store indique que ya ha acabado (evento ‘load’).

Sólo habría que cambiar el xtype de nuestro combo a ‘safecombo’ y siempre se realizará la carga de forma correcta.

4 Respuestas a “ComboBox en Sencha ExtJS 4. Carga asíncrona

  1. Ara 13 junio 2012 en 5:04 pm

    Hola.. soy nueva en extjs y estoy realizando combox dependientes.. pero no em cargan dlos datos.. y en el firebug me dice url undefined.. podrias ayudarme.. gracias!!

  2. Núria 9 enero 2013 en 1:55 pm

    ¡Excelente! Llevaba buscando esto horas y horas. Muchas gracias por ayudar a los demás a ver las cosas más claras.

  3. Isidoro 4 febrero 2013 en 7:48 pm

    Muchísimas gracias por esta explicación tan clara. Estoy comenzando con ExtJS y, aunque hay mucha documentación no es nada didáctica.

  4. Carlos Castaneda 16 febrero 2013 en 7:25 am

    woooowww, mejor se arruina amigo, muchisimas gracias, me funcionó increible con un combo con mas de 6000 registros en un renderer de un grid que pegaba el patinon bien feo, por la cantidad de registros, pero con esto… santo remedio. Saludos.

Responder

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Cerrar sesión / Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Cerrar sesión / Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Cerrar sesión / Cambiar )

Google+ photo

Estás comentando usando tu cuenta de Google+. Cerrar sesión / Cambiar )

Conectando a %s

A %d blogueros les gusta esto: