Guardar los cambios del Grid editable usando Ajax
Sep 03, 2009 | Español | By Crysfel | 25 CommentsEn el tutorial de hoy veremos como guardar la información de un Grid editable utilizando el componente Ajax que viene con el Framework, también mostraré como agregar nuevos registros al Grid y guardarlos en el servidor.
En tutoriales anteriores hemos visto como crear un Grid con capacidad de editar las celdas mediante una caja de texto o un Combobox, pero realmente eso no nos sirve de mucho si no guardamos esos cambios en nuestro servidor, el día de hoy voy a mostrar la manera tradicional de guardar estos cambios.
Es importante mencionar que en Ext 3 hay una manera más sencilla de hacer esto (utilizando un “Writter”), pero quiero mostrar el método “tradicional” que usábamos en la versión de Ext 2 porque creo que es importante para tener una compresión clara de lo que sucede ya que en futuros tutoriales mostraré una forma más sencilla.
Material de apoyo
Para comenzar es necesario descargar el material de apoyo que consta de un archivo HTML donde únicamente se importa el Framework y hay clases “CSS” definidas, también encontrarás un archivo “JS” donde he definido el “namespace” y además algunos métodos que implementaremos en este tutorial, viene también un archivo “PHP” el cual nos servirá para simular el guardado y actualizado de información y por último una carpeta “icons” que contiene unas imágenes que mostraremos en los botones.
Demostración
He preparado una demostración de lo que haremos al término de este tutorial, te invito a que la pruebes por ti mismo.
Demostración
Guardar la información en el servidor
Para hacer más sencillo este ejemplo no voy a guardar la información en una base de datos, pues he de suponer que sabes como hacerlo así que voy a utilizar las “sesiones” para simular que guardamos y actualizamos la información, el código para lograr esto es el siguiente:
<?php
header("Content-Type: text/plain");
session_start();
$add = $_POST['records'];
if(!isset($_SESSION['data'])){
$data = array( //creates the initial data
'success'=>true,
'total'=>11,
'data'=>array(
array('id'=>1,'name'=>'John doe','age'=>23,'country'=>'USA'),
array('id'=>2,'name'=>'Taylor Swift','age'=>19,'country'=>'USA'),
array('id'=>3,'name'=>'Carlos Mena','age'=>22,'country'=>'México'),
array('id'=>4,'name'=>'Christiano Ronaldo','age'=>24,'country'=>'Portugal'),
array('id'=>5,'name'=>'Sasha Cohen','age'=>25,'country'=>'USA'),
array('id'=>6,'name'=>'Christian Van Der Henst','age'=>27,'country'=>'Guatemala'),
array('id'=>7,'name'=>'Collis Ta\'eed','age'=>31,'country'=>'USA')
)
);
$_SESSION['data'] = $data; //load the data in sessions for the first time
}else{
$data = $_SESSION['data']; //get the data if exist in session
}
if(isset($add)){ //if there are records to insert/update
$records = json_decode(stripslashes($add)); //parse the string to PHP objects
$ids = array();
foreach($records as $record){
if(isset($record->newRecordId)){ //records to insert
$id = count($data['data']);
$info = array(
'id'=> id,
'name'=> $record->name,
'age'=> $record->age,
'country'=> $record->country
);
array_push($data['data'],$info); //add the new record to session
array_push($ids,array('oldId'=>$record->newRecordId,'id'=>$id));//new id
}else{ //records to update
foreach($data['data'] as $key=>$r){ //search the record to update
if($r['id'] == $record->id){
$data['data'][$key]['name'] = $record->name; //update the properties
$data['data'][$key]['age'] = $record->age;
$data['data'][$key]['country'] = $record->country;
break;
}
}
}
}
//print the success message
echo json_encode(array(
'success'=>true,
'data'=>$ids
));
}else{
//print all records in session
echo json_encode($data);
}
?>
He comentado los puntos más importantes en el código, recuerda que es solo un ejemplo, en el mundo real deberías guardar la información en una base de datos o enviarlas a un WebService, etc. cuando implementes tu “controller” debes tener en cuenta que éste debe hacer dos cosas (por ahora).
- Si recibe registros (mediante el parámetro “post” llamado “records”) debe insertarlos o actualizarlos, esto depende de la variable “newRecordId” ya que si existe nos indica que es un registro nuevo y tiene que insertarse de lo contrario es un registro existente y tiene que actualizarse.
- Si no recibe registros solamente debe desplegar los registros que existen.
Creación del Grid editable
En este momento vamos a crear el Grid editable con los campos que definimos en el código anterior; escribiremos en el método “init” el siguiente código:
init: function(){
//main code
//creates the store for the grid
var store = new Ext.data.JsonStore({
url: 'editorgrid-ajax.php',
root: 'data',
id:'id',
fields: ['name','age','country']
});
//load the data
store.load();
//creates the texfield to edit the data
var textField = new Ext.form.TextField();
var numberField = new Ext.form.NumberField({allowBlank:false});
//creates the editor grid
this.grid = new Ext.grid.EditorGridPanel({
store: store,
columns: [
new Ext.grid.RowNumberer(),
{header:'Name', dataIndex:'name',sortable: true,width:145,editor:textField},
{header:'Age', dataIndex:'age',sortable: true, editor:numberField},
{header:'Country', dataIndex:'country',sortable: true, editor:textField}
],
border: false,
stripeRows: true
});
//creates a window to hold the grid
var win = new Ext.Window({
title: 'Editor Grid example',
layout: 'fit',
width: 410,
height:350,
items: this.grid
});
//show the window and the grid
win.show();
},
No me detendré a explicar el código anterior pues ya lo hice en los tutoriales anteriores, así que si no tienes idea de lo que pasó, te recomiendo comenzar con los tutoriales pasados.
Hasta ahora deberíamos ver en pantalla algo como la siguiente imagen:
Grid editable
Guardar los registros modificados
Ahora si viene la parte interesante de este tutorial, pero primero necesitamos crear un botón para que al ser presionado por el usuario enviemos la información al servidor mediante Ajax, también vamos a crear un “ToolBar” donde estarán los botones que usaremos.
var win = new Ext.Window({
title: 'Editor Grid example',
tbar:{
defaults:{scope:this},
items:[
{text:'Save changes',iconCls:'save-icon',handler:this.save}
]
},
layout: 'fit',
width: 410,
height:350,
items: this.grid
});
Toobar en la ventana contenedora
Solamente quiero hacer notar que mediante la propiedad “handler” definimos la función que se ejecutará cuando el usuario de clic sobre el botón, además mediante la propiedad “default” podemos aplicar la propiedad “scope” a todos los botones que definamos, es importante definir el “scope” para la correcta ejecución de la función.
Dentro de la función “save” escribiremos el siguiente código:
//save changes in the grid
var modified = this.grid.getStore().getModifiedRecords();//step 1
if(!Ext.isEmpty(modified)){
var recordsToSend = [];
Ext.each(modified, function(record) { //step 2
recordsToSend.push(Ext.apply({id:record.id},record.data));
});
this.grid.el.mask('Saving…', 'x-mask-loading'); //step 3
this.grid.stopEditing();
recordsToSend = Ext.encode(recordsToSend); //step 4
Ext.Ajax.request({ // step 5
url : 'editorgrid-ajax.php',
params :{records : recordsToSend},
scope:this,
success : function(response) { //step 6
this.grid.el.unmask();
this.grid.getStore().commitChanges();
}
});
}
En el paso 1 se recogen los registros que han sido modificados, esto nos regresa un arreglo con cada “record” que se modificó, luego verificamos que no este vacío.
En el paso 2 iteramos el arreglo de registros modificados y vamos agregándolos a un arreglo que se llama “recordsToSend” en formato JSON, además incluimos el “id” ya que éste no se encuentra con el resto de la información.
En el paso 3 le ponemos una máscara al Grid con el mensaje de “Saving…” y le agregamos la clase CSS “x-mask-loading” la cual muestra una animación que denota que se esta procesando algo. Mediante el método “stopEditing” nos aseguramos que el usuario no este editando ningún otro campo.
En el paso 4 transformamos el “Array” a “String” para ser enviado al servidor en formato JSON.
En el paso 5 realizamos la petición al servidor por medio de Ajax, definimos la “url” que invocaremos así como la información que se enviará en el parámetro “records”, recuerda que cuando enviamos información utilizando la propiedad “params” la petición se realiza por el método “post” por defecto.
En el paso 6 definimos una función que se ejecutará cuando el servidor responda, dentro de esta función quitamos la máscara que cubría el Grid y hacemos un “commit” de los cambios ocurridos en el Grid, al hacer este “commit” los campos marcados como “sucios” (Dirty) dejarán de serlo.
Editando el contenido de las celdas
Enviando la información al servidor para ser guardada
La información ha sido guardada correctamente
Crear un registro nuevo
En la sección anterior logramos guardar los registros que fueron modificados por el usuario, ahora vamos a insertar nuevos registros en el Grid, para esto necesitamos crear un botón en la barra de herramientas que nos permita realizar lo antes mencionado.
var win = new Ext.Window({
title: 'Editor Grid example',
tbar:{
defaults:{scope:this},
items:[
{text:'Save changes',iconCls:'save-icon',handler:this.save},
{text:'Add Person',iconCls:'add-icon',handler:this.add} // add button
]
},
layout: 'fit',
width: 410,
height:350,
items: this.grid
});
Ahora necesitamos implementar el método “add” que está invocando el botón cuando es presionado.
//add a new row to the grid
var position = this.grid.getStore().getCount();
var id = Ext.id();
var defaultData = { //step 1
newRecordId: id
};
var Person = this.grid.getStore().recordType; //step 2
var person = new Person(defaultData,id);
this.grid.stopEditing(); //step 3
this.grid.getStore().insert(position, person); // step 4
this.grid.startEditing(position, 1); //step 5
El código anterior debe ser escrito dentro del método “add”.
En el paso 1 se crea un objeto con la información por defecto, en este caso únicamente se está definiendo la propiedad “newRecordId” la cual es utilizada por el servidor para saber si el registro lo debe insertar o actualizar en la base de datos, el valor de esta propiedad ha sido generado mediante “Ext.id()” para tener un id único.
El paso 2 es muy importante y es necesario porque cuando creamos el Store del grid nosotros no definimos ni el “Reader”, ni el “Record” directamente, esto lo hizo “Ext” automáticamente, por lo tanto no tenemos una referencia al “Record” generado, gracias a la propiedad “recordType” del “store” accedemos al “Record” que fue creado por el Framework dinámicamente, una vez que tenemos la referencia al componente “Record” creamos una instancia de éste con la información que generamos en el paso 1 y le asignamos su “id”.
En el paso 3 solamente detenemos cualquier edición activa por parte del usuario.
El paso 4 es el que hace la “magia”, ya que es aquí donde se inserta el nuevo “record” al store del Grid y automáticamente se reflejará la nueva fila vacía en el Grid, el primer parámetro que recibe este método es la posición donde queremos que se inserte, la posición la hemos calculado previamente contando el total de filas existentes esto nos da como resultado que la fila se inserte hasta el final del grid.
En el paso 5 solamente hacemos que de la fila que acabamos de insertar en la columna número “1” aparezca una caja de texto (que definimos al crear el Grid) para editar el contenido de la celda, es importante mencionar que la columna “0” para este caso es la columna donde están los números, en caso de no tener esta columna podríamos comenzar a editar la columna “0”.
Al actualizar nuestro explorador debería ver algo como la siguiente imagen en la pantalla:
Agregando una fila al Grid
Para guardar la información que insertemos en el registro nuevo no tenemos que hacer nada puesto que ya se debería guardar utilizando el método “save” que hicimos anteriormente.
Guardando él registro nuevo
Si has notado se esta enviando al servidor el registro a insertar con la propiedad “newRecordId”, además el “id” asignado no es el mismo “id” que le asigna el servidor, esto lo sabemos porque en la respuesta el servidor nos dice el “id” que recibió y el “id” que asignó.
Parámetros enviado al servidor por Ajax
Lo que debemos hacer ahora es modificar el método “save” para que sincronice los “id’s” correctamente. Dentro de la función “success” escribiremos el siguiente código:
//update the records with the correct ID's
var info = Ext.decode(response.responseText); // step 1
Ext.each(info.data,function(obj){
var record = this.grid.getStore().getById(obj.oldId); //step 2
record.set('id',obj.id); //step 3
delete record.data.newRecordId; //step 4
},this);
En el paso 1 se está decodificando la información que nos regresó el servidor en formato JSON para poder manipularla fácilmente.
En el paso 2 se obtiene el “Record” utilizando el “id” que le fue asignado a la hora de crearlo.
En el paso 3 se actualiza su “id” por el “id” asignado por el servidor en nuestra base de datos.
El paso 4 es importante para eliminar la propiedad “newRecordId” que tiene el registro, de esta forma evitamos que se inserte nuevamente si solamente queremos actualizarlo.
Cancelar los cambios
Por defecto al editar el contenido de las celdas de un Grid éstas quedan en un estado de “Dirty”, en este estado nosotros podemos deshacer los cambios y regresar a la información original.
Primero creamos el botón que nos permitirá realizar la acción pertinente.
var win = new Ext.Window({
title: 'Editor Grid example',
tbar:{
defaults:{scope:this},
items:[
{text:'Save changes',iconCls:'save-icon',handler:this.save},
{text:'Add Person',iconCls:'add-icon',handler:this.add},
{text:'Cancel changes',iconCls:'cancel-icon',handler:this.cancel} //cancel changes
]
},
layout: 'fit',
width: 410,
height:350,
items: this.grid
});
Botón para cancelar los cambios
Luego implementamos el método “cancel” de la siguiente manera:
cancel: function(){
//cancel the changes in the grid
this.grid.getStore().rejectChanges();
}
Cuando invocamos el método “rejectChanges” del store, automáticamente regresan al estado original las celdas modificadas, esto es suficiente para deshacer los cambios ocurridos.
Conclusiones
En este tutorial aprendimos como guardar información a través de Ajax, también como insertar y actualizar los registros de un store y verlos reflejados en un Grid editable, en futuros tutoriales mostraré como hacerlo de una manera más sencilla utilizando las nuevas características de Ext 3.
Si tienes alguna duda o sugerencia puedes hacerlo en los comentarios, recuerda que tenemos un foro donde la comunidad está creciendo y la participación es muy buena, te invito también a seguirnos en Twitter (@quizzpot) para estar al tanto de las actualizaciones del sitio.







Gracias.