Introducción a los Grids en ExtJS
18/11/2008 por Javier Caride
En ExtJS tenemos básicamente dos tipos de grids, los que únicamente son para listar información (los llamaremos Grids básicos) y los grids editables, que permiten editar la información que se lista dentro de ellos.
Grid básico
En los grids tenemos que distinguir 3 partes importantes:
- El store de datos
- El modelo de datos de las columnas
- La definición del grid en sí
El store de datos, es el objeto que se encarga de almacenar los datos que se van a mostrar en el grid. Básicamete se tratan de arrays de datos, pero su particularidad es que pueden leer los datos desde diversas fuentes:
- Estáticas: ficheros XML o variables de tipo array
- Dinámicas: servicios de datos XML o JSON
En la definición de un store tenemos que tener en cuenta dos aspectos importantes: el formato de los datos, que nos va a indicar qué tipo de reader necesitamos configurar y si el origen de dato es remoto, configurar el proxy de lectura de datos
El modelo de datos de las columnas nos define qué datos se van a mostrar en el grid, con qué formato, y de donde se extraen los datos. Hay que tener en cuenta que aunque en un registro se lean muchos campos, podemos elegir cuales mostrar y cuales no.
Pasos para configurar un grid
Primero definimos el formato de cada registro, aquí configuramos cuantos campos hay y de que tipo
/*
* Creamos el registro de datos
*/
var categoriesRecord = new Ext.data.Record.create([
{name: 'cad_id', type: 'int'},
{name: 'cad_name', type: 'string'},
{name: 'cad_date', type: 'date', dateFormat: 'Y-m-d'},
{name: 'cad_description', type: 'string'}
]);
Después, configuramos el lector de datos. Como se dijo antes, el lector de datos será diferente dependiendo del formato de los datos que obtengamos. En este caso estamos definiendo que los datos nos vendrán codificados en JSON
/*
* Creamos el reader de datos
*/
var categoriesGridReader = new Ext.data.JsonReader({
root: 'data',
totalProperty: 'total',
id: 'cad_id'},
categoriesRecord
);
La configuración del reader es fácil de entender:
- Mediante el campo root, indicamos la etiqueta bajo la que llegarán los datos, en este caso ‘data’
- El campo totalProperty nos indica en qué etiqueta nos llega el número total de registros
- El campo id, nos indica en qué campo nos llega la identificación del registro
Una vez tenemos configurado el registro y el reader, definimos el proxy de datos (para una carga remota de los mismos).
/*
* Creamos el proxy para lectura remota de datos
*/
var categoriesDataProxy = new Ext.data.HttpProxy({
url: 'scripts/categoriesService.php', // Servicio web
method: 'POST' // Método de envío
});
En este caso, vemos que la carga la hacemos a través de un script php.El cual nos debe devolver un JSON como el siguiente:
{
"total":3,
"data":[
{
"cad_id":1,
"cad_name":"Categoría 1",
"cad_description":"Descripción de la primera categoría",
"cad_date": "2008-10-01"
},{
"cad_id":2,
"cad_name":"Categoría 2",
"cad_description":"Descripción de la segunda categoría",
"cad_date": "2008-10-11"
},{
"cad_id":3,
"cad_name":"Categoría 3",
"cad_description":"Descripción de la tercera categoría",
"cad_date": "2008-10-20"
},{
"cad_id":4,
"cad_name":"Categoría 4",
"cad_description":"Descripción de la cuarta categoría",
"cad_date": "2008-10-21"
},
]
}
Para ello, el php de prueba tiene este contenido
$retValue = array(
'total' => 4,
'data' => array(
array(
'cad_id' => 1,
'cad_name' => 'Categoría 1',
'cad_description' => 'Descripción de la primera categoría',
'cad_date' => '2008-10-01'
),
array(
'cad_id' => 2,
'cad_name' => 'Categoría 2',
'cad_description' => 'Descripción de la segunda categoría',
'cad_date' => '2008-10-11'
),
array(
'cad_id' => 3,
'cad_name' => 'Categoría 3',
'cad_description' => 'Descripción de la tercera categoría',
'cad_date' => '2008-10-20'
),
array(
'cad_id' => 4,
'cad_name' => 'Categoría 4',
'cad_description' => 'Descripción de la cuarta categoría',
'cad_date' => '2008-10-21'
)
)
);
echo json_encode($retValue);
En el caso de scripts php, primero hay que crear en un array la estructura que queramos devolver y después codificar mediante al función json_encode (PHP5), o bien (en PHP4) utilizar la librería de codificación JSON que provee PEAR
Hay que tener en cuenta una cosa JSON, trabaja únicamente en UTF-8, por lo que si los datos los extraemos por ejemplo de una base de datos configurada en ISO-8859-1, HAY QUE CONVERTIRLOS A UTF-8, obligatoriamente.
Ahora ya tenemos todos los elementos necesarios para configurar el store del grid:
/*
* Creamos el datastore donde se van a almacenar los datos de la tabla
*/
var categoriesDataStore = new Ext.data.Store({
id: 'categoriesDS',
//Indicamos de donde se va a leer los datos, en este caso un servicio web
proxy: categoriesDataProxy,
// Parámetros base que se enviarán al script
baseParams: {
language: "es_ES"
},
// Indicamos el reader, es decir el procesador de los datos
reader: categoriesGridReader
});
Ya por último, antes de definir el grid en sí, definimos cuales van a ser nuestras columnas, su tipo, ancho, etc.
var categoriesColumnMode = new Ext.grid.ColumnModel(
[{
header: '#',
dataIndex: 'cad_id',
width: 50,
hidden: false
},{
header: 'Categoría',
dataIndex: 'cad_name',
width: 150
},{
header: 'Descripción',
dataIndex: 'cad_description',
width: 300
},{
header: 'Fecha',
dataIndex: 'cad_date',
width: 90,
renderer: Ext.util.Format.dateRenderer('d/m/Y')
}]
);
Ahora ya tenemos todos los elementos necesarios para configurar el grid:
var categoriesGrid = new Ext.grid.GridPanel({
id: 'cat_categoriesGrid',
store: categoriesDataStore,
cm: categoriesColumnMode,
enableColLock:false,
selModel: new Ext.grid.RowSelectionModel({singleSelect:false})
});
Sólo nos falta poner todo dentro de un bloque Ext.onReady y definir una ventana para poder visualizar el grid.
Grid editable
Además de los grids básicos, ExtJS permite definir grids en los que se puedan editar valores. Para convertir un grid no editable a uno editable, tenemos que hacer cambios en dos lugares: en el ColumnModel y en la definición del grid En el Column mode, hay que incluir una etiqueta ‘editor’ en aquellas columnas que queramos hacer editables. En esta etiqueta los que se pone es la definición de un campo editable cuyo objeto puede ser cualquiera de los que hay disponibles para los formularios (cajas de texto, combos, fechas, tiempo…)
var categoriesColumnMode = new Ext.grid.ColumnModel(
[{
header: '#',
dataIndex: 'cad_id',
width: 50,
hidden: false
},{
header: 'Categoría',
dataIndex: 'cad_name',
width: 150,
editor: new Ext.form.TextField({})
},{
header: 'Descripción',
dataIndex: 'cad_description',
width: 300,
editor: new Ext.form.TextField({})
},{
header: 'Fecha',
dataIndex: 'cad_date',
width: 90,
renderer: Ext.util.Format.dateRenderer('d/m/Y'),
editor: new Ext.form.DateField({})
}]
);
La modificación del grid es “más fácil”. En este caso sólo hay que heredar de otra clase distinta: EditorGridPanel
var categoriesGrid = new Ext.grid.EditorGridPanel({
id: 'cat_categoriesGrid',
store: categoriesDataStore,
cm: categoriesColumnMode,
enableColLock:false,
selModel: new Ext.grid.RowSelectionModel({singleSelect:false})
});
Al ejecutar el ejemplo, podemos ver que al hacer doble click en cualquiera de los campos editables, se nos permite cambiar el valor del campo y que dicho cambio queda marcado en la esquina superior izquierda con un pequeño icono rojo
Tratando los cambios en un grid editable
De nada nos sirve tener un grid editable si no tratamos los datos, pues todos los cambios que se hagan en un grid editable son cambios en el store local.
ExtJS no ofrece ningún mecanismo para automatizar el guardado de datos, dejando a elección del programador la elección del método. Por ejemplo, podemos hacer actualizaciones campo a campo, capturando el evento de cambio de una celda, o bien podemos enviar todos los cambios de una vez mediante la acción en algún botón o componente
Qué método utilizar queda a elección del desarrollador según las circunstancias en las que se encuentre. En este caso vamos a explicar cómo se haría un envío de todos los cambios. Para ello vamos a coger el ejemplo anterior y primeramente añadir un botón de actualización de cambio situado en una barra de herramientas superior
var categoriesGridTopbar = new Ext.Toolbar({
id: 'categoriesGridTopbarId',
items: [
'->',
{
xtype:'button',
text:'Guardar Cambios',
id: 'portadas_ToolbarTop_btn0',
handler:categoriesGridTopbarHnd,
type:'button'
}
]
});
Como se puede ver, se a asignado un handler al botón, que será el que tenderá la petición de guardado de cambios y que contendrá toda la lógica de actualización
En el handler, lo que hemos hecho es crear una llamada Ajax que envíe al servidor todos los cambios, para ello se pide al store un array con todos los registros que han cambiado, se recorre y se almacena en un array los datos necesarios para enviar. Este array se codifica en JSON y se envía como una variable POST normal. Después es tarea del script php (o Java, o lo que sea) de tratar el array de cambios y realizar las actualizaciones pertinentes.
var failureAjaxFn = function(response, request) {
Ext.Msg.show({
title: 'Error',
msg: 'Cambios no guardados. Status:' + response.status,
buttons: Ext.Msg.OK,
animEl: 'elId',
icon: Ext.MessageBox.ERROR
});
}
var successAjaxFn = function(response, request) {
/*
* En response.responseText tenemos la respuesta del script, en este caso
* la salida del script PHP es JSON con lo que tenemos que decodificarlo
*/
var jsonData = Ext.util.JSON.decode(response.responseText);
if (true == jsonData.success) {
Ext.Msg.show({
title: 'Operación completada',
msg: jsonData.message,
buttons: Ext.Msg.OK,
animEl: 'elId',
icon: Ext.MessageBox.INFO
});
/*
* PASO IMPORTANTE
* Para indicar al store que los cambios se han realizado correctamente
* y eliminar los indicadores de que hay cambios pendientes de guardar
* hay que llamar
*/
categoriesDataStore.commitChanges();
} else {
Ext.Msg.show({
title: 'Error',
msg: jsonData.message,
buttons: Ext.Msg.OK,
animEl: 'elId',
icon: Ext.MessageBox.ERROR
});
}
}
var categoriesGridTopbarHnd = function (e,o) {
/*
* Pedimos al store que nos devuelva los registros modificados
*/
var modifiedRecords = categoriesDataStore.getModifiedRecords()
if (modifiedRecords.length > 0) {
/*
* Creamos un array con los datos de los registros que han cambiado
*/
var changes = new Array();
for (var i=0; i < modifiedRecords.length; i++){
changes.push(modifiedRecords[i].data);
}
/*
* Codificamos los cambios en JSON
*/
changes = Ext.util.JSON.encode(changes);
Ext.Ajax.request({
url: 'salvarDatos.php',
method: 'POST',
success: successAjaxFn,
failure: failureAjaxFn,
timeout: 30000,
params: {
operation: 'update',
changes: changes
}
});
}
}
Grids paginados
Uno de las barras de herramientas más utilizadas con los grids es la de paginación. Su utilización es
bastante sencilla. Primero creamos la barra de paginación:
var categoriesPagingBar = new Ext.PagingToolbar({
pageSize: 10,
store: categoriesDataStore,
displayInfo: true
});
Su uso es sencillo, sólo tenemos que indicar cual es el store, y el tamaño de página. Después de definirlo
se lo asociamos al grid, para lo que sólo tenemos que indicar en la configuración
lo siguiente “bbar: categoriesPagingBar”:
var categoriesGrid = new Ext.grid.EditorGridPanel({
id: 'cat_categoriesGrid',
store: categoriesDataStore,
cm: categoriesColumnMode,
enableColLock:false,
tbar: categoriesGridTopbar,
bbar: categoriesPagingBar,
selModel: new Ext.grid.RowSelectionModel({singleSelect:false})
});
Después en la carga inicial de datos, indicamos que queremos cargar la primera página de resultados
y el número de ellos
categoriesDataStore.load({params: {start: 0, limit: 10}});
Y después de esto ya no nos tenemos que preocupar de nada más, pues cada vez que hagamos click en los botones el store nos enviará dos variables nuevas: start y limit, al estilo de las paginaciones clásicas
En el for te faltan 3 corchetes de cierre
var categoriesGridTopbarHnd = function (e,o) {
…….
Bueno de por si hay varios errores en el script
Gracias Nando,
Acabo de ver lo que dices. No sé porqué el código no sale completo. Voy a revisar el plugin de “coloreado” del código a ver que está haciendo.
Gracias por avisar
Ya está solucionado Nando, estaba cortado el código por un fallo.
Gracias de nuevo
Grax por esta explicacion :D
La verdad es que esta muy bien explicado el codigo, de hecho a partir de este codigo es que aprendi a implementar un grid y a entender el funcionamiento de extjs, gracias Javier
Te felicito por el trabajo, es muy clara tu manera de enseñar. No soy programador y la unica forma de aprender a usar extJS es con ejemplos asi y bien comentados.
Igualmente el ejemplo puntual que necesito no lo encuentro todavia: quiero guardar los datos de los campos de un formulario en una DB MySQL, logro conectarme a la DB, pero genera campos vacios en la tabla, es decir no pasa los datos pero si crea la nueva fila de campos. El php funciona bien porque lo probe con un formulario comun en html y carga los datos.
¿Sabes de algun lugar donde pueda ver un ejemplo en extJS de un formulario conectado a una DB?
Gracias por tu dedicación.
Estube practicando un poco con este tuto que dejas y luego de reventarme la cabeza porque el grid se mostraba pero no sus datos, me di cuenta que te falta una linea de codigo importante y es ejecutar el metodo load() de la clase Ext.data.Store para asi lograr hacer la peticion y traer como respuesta los datos en formato JSON. Solo hace falta agregar la siguiente linea despues de definir la variable categoriesDataStore:
categoriesDataStore.load();
Gracias!!
Hola muy buen ejemplo ..pero tengo una duda al respecto …..despues que se envia el arreglo con los cambios al script ..como yo se lo que llego porque e tratado varias maneras y no alcanzo dar con la verdad..
Hola exelente tutoriales por aqui!
Felicidades…
Darlyn la manera correcta es debugeando el codigo , ahi podras ver las respuestas y peticiones , en este caso las del php , busca el firebug, lee un manual o travesealo es facil
saludos
Buen manual , gracias por buena explicacion , estoy tratando de cargar los datos del grid desde PHP , pero realmente no se como hacerlo , existe algun ejemplo o ayuda para esto
te lo agradeceria mucho
Juan C
consulta,porque cuando mando parametros de sort de la grilla estos van por request y no por post o get,como lo defino
Me parece genial y comprendo perfectamente todo gracias a ti ahora, pero aun no logro encontrar nadie!! que brinde un ejemplo usando JSP en vez de PHP, si conoces algo pequeño ya sea para combobox o parecidos, te ruego lo compartas.
Saludos.
[...] http://www.desarrollosweb.net/2008/11/18/introduccion-a-los-grids-en-extjs/ [...]
Muchas gracias por esta explicación tan clara en un tema tan importante. Esto permite unir el mundo cliente/JS con el lado servidor/PHP o en mi caso servidor/appengine-python. Es justo lo que andaba buscando.
¿Como manejar cuando la consulta a la base de datos no retorna datos, el gridPanel no se recarga y se mantienen los datos anteriores. como limpiar el grid y detectar que no llegaron datos?
Cual es el php para guardar los datos, como seria?
Muchas Gracias exelente informacion
Esta muy bueno el ejemplo, lo probe y me pincha bien, con la paginacion tengo un problema me carga todos los datos , si me indica bien el numero de paginas pero me carga toda la informacion en todas las paginas. Lo tengo igual que ustedes , pienso qeu el error este en el codigo php porque to cargo los datos de una base de datos.
Administradores();
$retValue = array(
‘total’ => count($ret),
‘data’ => $ret
);
echo json_encode(array_slice($retValue, 0, 4));
?>
Si alguien me pudiera ayudar se lo voy agadecer porque soy nuevo en extjs y no tengo
muchas personas que me puedadn ayudar a mi alrededor
Gracias el foro esta muy bueno
Esta super la explicacion ahora quisiera saber si han logrado crear las columnas del grid dinamicamente.O sea tengo una tabla en la que a medida que inserto valores crecen la cantidad de columnas de un grid.
Saludos a todos.
Quisiera que me ayuden con un problema con el grid editable, el error es que: Al insertar una fila en el grid editable se inserta la fila pero se me insertan en los campos de texto y combobox que tengo como editores lo siguiente: . Mientras que en los campos NumberField no me da ese problema.
Gracias por sus sugerencias
Disculpen lo que se inserta es el codigo de un espacio en blanco  
Fredy:
Seguramente te muestra todos los datos porque tu consulta a la base de datos la estas haciendo como un SELECT * FROM, deberia ser un SELECT * FROM tabla LIMIT 10 OFFSET 0 (en mysql)
Genial! El mejor tutorial para empezar a comprender algo de esto… A ver si aprender en la documentación oficial!
saben hice un grid que hace operaciones como una pagina excell pero resulta que quiero saber dos cosas:
1. como hago para pasar con un enter a la siguiente fila y
2. cuando tengo un celda en 0 y le coloco otro monto sin darle enter me sale una marca de color rojo en la esquina superior izquierda y despues no me suma toa la columna mas bien me sale error. Pero si le coloco el monto y le doy enter si me suma correctamente. Como hago para que cuando el usuario digite el monto y luego se mueva con el mouse a otro campo no me de error ????
si pueden me escriben a mi privado hicolu@yahoo.com
enormemente agradecido
Freddy gracias por responder ya inserte la linea de codigo y nada, todo sigue igual!!! :(
Hola,
Excelente guia, se aprende bastante bien, por si alguien quiera conocer sobre JEE abajo dejo el link,
En relacion al ejemplo me esta dando errores y no mu muestra los datos
El error es el siguiente:
Error: E.fireFn is undefined
Archivo de origen: http://localhost:8080/ERPWeb/js/ext-2.0/ext-all.js
Línea: 12
Saben por que puede ser?
Gracias
http://www.marcosjara.com