Aprendiendo Ext JS 3

Editar una celda mediante un Combobox Más videos

Descripción del tema

Normalmente tenemos relaciones en nuestras tablas de la base de datos y una de las mejores maneras para mostrárselas al usuario es mediante un Combobox que despliegue los registros de la tabla que necesitamos relacionar. Ya vimos como editar el contenido de las celdas mediante una caja de texto, en este tutorial vamos a crear una Grid que nos permita editar el contenido de una celda utilizando un Combobox para seleccionar una de varias opciones.

Demostración

He preparado una demostración de lo que haremos al final del tutorial, te invito a que la pruebes para que tengas en mente el objetivo final.
grid con combo editable

Demostración del tutorial

Material de apoyo

Antes de continuar te recomiendo descargar el material de apoyo que he preparado, recuerda que se tiene que copiar dentro de un servidor Web, en una carpeta que se llama “editorgrid” donde estamos trabajando. Puedes ver que en el material de apoyo vienen dos archivos PHP, esto es para simular dos diferentes “controllers” que nos regresan información diferente, uno de ellos (editorgrid-combo.php) nos regresa la información que irá en el Grid y el otro (editorgrid-combo-cmb.php) la información con la que llenaremos las opciones del combo.

Información del Grid

Lo primero que debemos hacer es definir la manera en que recibiremos la información desde nuestro “controller” en este caso “editorgrid-combo.php”.
<?php
	header("Content-Type: text/plain"); 
	
	$data = array(
			array('genre'=>2,'title'=>'G-Force','year'=>2009,'weekend'=>32.2,'allTime'=>32.2),
			array('genre'=>5,'title'=>'Harry Potter and the Half-Blood Prince','year'=>2009,'weekend'=>30,'allTime'=>222),
			array('genre'=>3,'title'=>'The Ugly Truth','year'=>2009,'weekend'=>27,'allTime'=>27),
			array('genre'=>4,'title'=>'Orphan','year'=>2009,'weekend'=>12.8,'allTime'=>12.8),
			array('genre'=>2,'title'=>'Ice Age: Dawn of the Dinosaurs ','year'=>2009,'weekend'=>8.2,'allTime'=>171),
			array('genre'=>1,'title'=>'Transformers: Revenge of the Fallen','year'=>2009,'weekend'=>8,'allTime'=>379),
			array('genre'=>3,'title'=>'The Hangover','year'=>2009,'weekend'=>6.46,'allTime'=>247),
			array('genre'=>3,'title'=>'The Proposal','year'=>2009,'weekend'=>6.42,'allTime'=>140),
			array('genre'=>1,'title'=>'Public Enemies','year'=>2009,'weekend'=>4.17,'allTime'=>88.1),
			array('genre'=>8,'title'=>'Brüno','year'=>2009,'weekend'=>2.72,'allTime'=>56.5)
			
	);
		
	
	echo json_encode(array(
		'success'=>true,
		'total'=>count($data),
		'data'=> $data
	));
?>
He decidido utilizar JSON para el intercambio de información, además se definieron los campos que desplegaremos en el Grid.

Crear el Grid editable

Con el código anterior en mente vamos a crear el Grid Editable, no me voy a detener a explicar este paso pues ya lo hice en repetidas ocasiones en temas pasados.
var store = new Ext.data.JsonStore({ // 1
	url: 'editorgrid-combo.php',
	root: 'data',
	totalProperty: 'total',
	fields: ['genre','title','year','weekend','allTime']
});
store.load(); //2

var grid = new Ext.grid.EditorGridPanel({ //3
	store: store,
	columns: [
		new Ext.grid.RowNumberer(),
		{header:'Genre', dataIndex:'genre',width:100,sortable: true},
		{header:'Title', dataIndex:'title', width:200,sortable: true},
		{header:'Year', dataIndex:'year', width:60,sortable: true},
		{header:'Weekend', dataIndex:'weekend', width:55,sortable: true},
		{header:'All Time', dataIndex:'allTime', width:55,sortable: true}
	],
	border: false,
	stripeRows: true
});
Primero creamos el “store” donde definimos los campos que contendrán los registros, además configuramos la “url” de donde proviene la información (1), después hacemos la petición al servidor mediante el método “load” (2) y por último creamos el Grid Editable con las columnas que necesitamos.

Crear la ventana contenedora

Hasta este punto no hemos visto nada en pantalla porque no se ha renderizado el Grid, vamos a usar una ventana para desplegarlos.
var win = new Ext.Window({
	title: 'USA Weekend Box-Office Summary',
	layout: 'fit',
	width: 520,
	height:300,
	items: grid
});
win.show();
grid con combo editable

Grid editable

Lo que hemos hecho debe estar perfectamente claro, pero si tienes alguna duda puedes ir a repasar los tutoriales anteriores donde hablo mas a detalle de lo sucedido.

Crear el combo para seleccionar los años

Vamos a crear nuestro primer combo, éste será muy sencillo y lo usaremos para permitir que el usuario pueda seleccionar un año.
var comboYear = {
	xtype: 'combo',
	triggerAction : 'all',
	store: [2009,2008,2007,2006]
}
Quiero hacer notar que el código anterior es solamente la configuración de un combo que desplegará cuatro años anteriores (2009, 2008, 2007, 2006), he decidido hacerlo estático para fines didácticos. Lo siguiente es asignar este combo a la columna donde necesitemos desplegarlo en el Grid Editable, esto lo hacemos mediante la propiedad “editor”.
{header:'Year', dataIndex:'year', width:60,sortable: true,editor:comboYear},
Ahora actualizamos el navegador y veremos algo como la siguiente imagen.
grid con combo editable

Grid editable con combo estático

Como puedes ver se ha desplegado un combo en el cual podemos seleccionar un año diferente, esto ha sido realmente sencillo, en tan solo unas pocas líneas de código lo hemos logrado.

Crear un combo dinámico

El combo que hicimos anteriormente era muy sencillo, en el mundo real no todo es tan sencillo, lo que vamos hacer ahora es crear un combo cuya información provenga de una tabla en la base de datos que se llame “Genres” que esta relacionada de uno a muchos (one-to-many) con otra que se llama “Movies”.
grid con combo editable

Diagráma de base de datos (relación "one-to-many")

Dada la imagen anterior podemos decir que “Un genero tiene muchas películas”. Necesitamos sacar la información de la tabla y exponerla mediante un “controller”, para este ejemplo he creado el siguiente archivo PHP encargado de mostrar la información para el combo (editorgrid-combo-cmb.php), es importante mencionar que para fines didácticos la información está en un arreglo, pero supongamos que esta información viene de la tabla “Genres” en la base de datos:
<?php
	header("Content-Type: text/plain"); 
	
	$data = array(
		array('value'=>1,'label'=>'Action'),
		array('value'=>2,'label'=>'Animation'),
		array('value'=>3,'label'=>'Comedy'),
		array('value'=>4,'label'=>'Drama'),
		array('value'=>5,'label'=>'Fantasy'),
		array('value'=>6,'label'=>'Horror'),
		array('value'=>7,'label'=>'Musical'),
		array('value'=>8,'label'=>'Romance')
	);
	
	
	echo json_encode(array(
		'records'=>$data
	));
?>
Con el código anterior en mente vamos a crear el combo que despliegue la información expuesta de la tabla “Genres” y asignárselo a la columna correspondiente en el Grid.
this.store = new Ext.data.JsonStore({
	root : 'records',
	fields : ['value','label'],
	url: 'editorgrid-combo-cmb.php' //diferent controller
});
this.store.load();
Primero creamos el store para el combo, puedes ver que la “url” es diferente al store del grid, esto es porque en el “controller” estamos consultamos la tabla “Genre” para llenar el Combobox, además estamos creando una variable “this.store” para tener una referencia al store que usaremos más adelante.
var comboGenre = new Ext.form.ComboBox({
	triggerAction : 'all',
	displayField : 'label',
	valueField : 'value',
	store : this.store
});
Por ultimo agregamos el combo a la columna donde se desplegará por medio de la propiedad “editor”.
{header:'Genre', dataIndex:'genre',width:100,sortable: true,editor:comboGenre},
Guardemos los cambios y actualicemos la página en el explorador y veremos algo semejante a la siguiente imagen.
grid con combo editable

Combobox dinámico en el grid editable

Puedes ver que al dar doble clic sobre alguna de las celdas de la columna “genre” se despliega el combo cuya información ha sido cargada dinámicamente desde una tabla en la base de datos y nos sirve para hacer la relación “one to many”.

Desplegar el género y no el identificador

Hasta ahora hemos desplegado la información de la tabla “Movies” en el Grid, pero si has notado esta apareciendo el “id_genre” (que es la llave foránea) en la primera columna, esto no es muy intuitivo y no nos sirve de nada, por lo tanto lo que debemos hacer es reemplazar ese identificador por su respectivo género. Lo que haremos para solucionar este problema es utilizar la propiedad “renderer” para cambiar el “id” por el texto que necesitamos.
{header:'Genre', dataIndex:'genre',width:100,sortable: true, editor:comboGenre, renderer:this.genre},
Ahora necesitamos crear la function “this.genre” que especificamos en el código anterior.
com.quizzpot.tutorial.ComboGridTutorial = {
	init: function(){		
		//código removido
	},
	//esta función es la que se ejecutará antes de
//que se renderize el contenido de la celda
	genre: function(value){
		
	},
	
	money: function(id){
		//código removido
	}
}
Ahora vamos a cambiar el valor por defecto (el “id”) por el respectivo texto, para esto vamos a utilizar el store del combo que contiene la información que necesitamos ¿recuerdan que les comenté que lo usaríamos más adelante? Pues aquí lo usaremos, por eso es que cuando lo definí hice que fuera una propiedad del objeto, de esta manera podemos usarlo dentro de esta función de la siguiente manera:
genre: function(id){
	var index = this.store.find('value',id);
	if(index>-1){
		var record = this.store.getAt(index);
		return record.get('label');
	}
	return value;
}
En el código anterior primero busco donde se encuentra el registro cuyo campo “value” (que es el “id_genre” en la tabla “Genres” de la base de datos) es igual al “id” que estoy recibiendo (la “llave foránea” que se encuentra en la tabla “Movies”), si encontró un registro (debería encontrarlo siempre, de lo contrarío nuestra base de datos no es consistente) lo saca del store para poder obtener el “label” del “id” correspondiente. Ahora si, todo debería funcionar correctamente, actualiza tu explorador y verás algo como lo siguiente.
grid con combo editable

Error en el scope

Cambiando el “scope”

Ops!! ¿Que pasó? Lo que ha sucedido es que la función que acabamos de escribir se está ejecutando en un “scope” diferente, por lo tanto al hacer referencia a la variable “this.store” nos dice que no existe, para solucionar este problema necesitamos especificarle el “scope” que usará al momento de asignársela a la columna, ya sea mediante la propiedad “scope” o utilizando el método “createDelegate”.
{
header:'Genre', dataIndex:'genre',width:100,sortable: true,
editor:comboGenre,renderer:this.genre,
scope: this //definimos el scope para la función “this.genre”
},
O también funcionaría así:
{
header:'Genre', dataIndex:'genre',width:100,sortable: true,
editor:comboGenre,
renderer:this.genre.createDelegate(this)//definimos el scope de la función
},
Ahora si probemos nuevamente el ejemplo en el navegador y debemos ver algo semejante a la siguiente imagen:
grid con combo editable

Cambiando el ID por un Texto

Asegurarse que siempre exista contenido en el store del combo

Por último debemos asegurarnos que se cargue primero el contenido del combo y luego el contenido del grid, debemos hacerlo así porque estamos usando el store del combo para renderizar el contenido del grid.
//una vez que se cargue el store del combo…
this.store.on('load',function(){
	store.load();//…cargamos el store del grid
});
Solamente necesitamos agregar un “listener” al evento “load” del store del combo para que inmediatamente después de que se cargue solicitemos la información del Grid, así evitamos que aparezcan los identificadores.

Conclusiones

El tutorial de hoy es muy importante, aprendimos como crear combos estáticos y asignárselos a las columnas para ser editadas, pero también aprendimos ha utilizar los combos para crear relaciones entre dos tablas de “one-to-many”, esto es muy común cuando desarrollamos sistemas, también hablamos un poco acerca del “scope”. De tarea les dejo que le agreguen campos de texto a las celdas que hacen falta y que le agreguen el “renderer: this.money” a las ultimas dos celdas donde se muestran cantidades de dinero. Si tienes dudas con respecto a este tutorial te recomiendo dejar tus comentarios o inscribirte en el foro, además si quieres recibir a tu correo estos tutoriales puedes inscribirte de forma gratuita a las Feeds.

Te gustaría recibir más tutoriales como este en tu correo?

Este tutorial pertenece al curso Aprendiendo Ext JS 3, te recomiendo revises el resto de los tutoriales ya que están en secuencia de menor a mayor complejidad.

Si deseas recibir más tutoriales como este en tu correo te recomiendo registrarte al curso, si ya eres miembro solo identifícate y registrate al curso, si no eres miembro te puedes registrar gratuitamente!

Si no gustas registrarte en este momento no es necesario! Aún así puedes recibir los nuevos tutoriales en tu correo! Jamás te enviaremos Spam y puedes cancelar tu suscripción en cualquier momento.

¿Olvidaste tu contraseña?

14Comentarios

  • Avatar-5 Jurasec 28/08/2009

    Sigo aprendiendo, gracias!. Por cierto, has pensado hacer un compilado de todos tus artículos en un solo pdf? Saludos

    • Avatar-5 Crysfel 30/08/2009

      Si, al finalizar el curso haré un libro con el material que he venido haciendo ;)

      • Avatar-11 henry 18/10/2010

        Saludos cordiales, Tengo un problema, el store del combo no se carga hasta que no se despliegue el combobox.

        • Avatar-5 Angélica 07/02/2011

          Hola Crys!!.... es justo lo que necesito... pero tengo un problema no lo puedo hacer correr... será por la versión de mi php 5.3... me este error en el explorador... Detalles de error de página web Agente de usuario: Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; WOW64; Trident/4.0; GTB6.6; Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1) ; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; InfoPath.2; OfficeLiveConnector.1.3; OfficeLivePatch.0.0) Fecha: Mon, 7 Feb 2011 00:12:07 UTC Mensaje: 'Ext' no está definido Línea: 1 Carácter: 1 Código: 0 URI: http://localhost/editorgrid/editorgrid-combo.js Por fa ayúdame... gracias mil....

          • Avatar-12 Crysfel 07/02/2011

            Asegurate que importes correctamenta la librería de ExtJS, parece que el error es ese, saludos

            • Avatar-5 Karo 15/02/2011

              Hola Crysfel Tengo un problema con mi combobox dinamico que esta dentro de la grid, me en lista los datos y los selecciono correctamente pero al momento de cambiarme a otro campo en la grilla en vez de mostrar el valor del displayField me muestra el valor del valueField (veo el id en vez del nombre). Gracias por tu ayuda

              • Avatar-9 Crysfel 15/02/2011

                Necesitas asignarle un "renderer" a la columnas donde saques el displayField del record del combo. Saludos

                • Avatar-9 Karo 16/02/2011

                  Crysfel, gracias por la ayuda pero soy nueva en esto y no tengo idea de como hacer un renderer, te agradeceria full si me ayudas con un link o algo donde me pueda guiar. Gracias gracias

                  • Avatar-11 Uriel Juarez Galvan 24/06/2011

                    Hola una pregunta mira lo que yo quiero hacer es que tengo un formulario con campos de texto que dependen de la seleccion que hago de un combobox por que los datos estan en una bd y esos datos deben aparecer completando varios de los campos de este formulario como le puedo hacer para que se despliegen en pantalla. Muchas Gracias

                    • Avatar-12 José Luis 15/09/2011

                      Hice todo como lo expones, el problema que a la función llega el texto y no el id del combo. Además tanto en el ejemplo como en el código descargado veo una inconsistencia ya que dentro de "com.quizzpot.tutorial.ComboGridTutorial = {" pones como parámetro de la función "genre", "value" y luego más abajo pones como parámetro "id" y devuelves "value" que no está definida en la función. De antemano gracias por responder.

                    • Avatar-2 kevin 27/02/2014

                      al poner el combo en la columna year, luego al elegir un valor del combo, este desaparece, pero al pinchar nuevamente sobre el combo aparece el ultimo escogido. ayuda

                      • Avatar-9 Josue Seijas 20/10/2016

                        Hola me da el siguiente error Uncaught TypeError: Cannot read property 'find' of undefined Funcion genre: function(id){ var index = this.store.find('value',id); if(index>-1){ var record = this.store.getAt(index); return record.get('label'); } return id; }, la linea donde se desplega el error es var index = this.store.find('value',id); Que puede ser? Gracias de antemano

                        • Avatar-7 Crysfel Villa 21/10/2016

                          Asegúrate de cambiar el scope de manera correcta cuando se llama a esa función: this.genre.createDelegate(this) Suerte!

                        Instructor del curso

                        Crysfel3

                        Autor: Crysfel Villa

                        Es ingeniero de software con más de 7 años de experiencia en desarrollo web.

                        Descarga Código Fuente Ver Demostración

                        Regístrate a este curso

                        Este tutorial pertenece al curso Aprendiendo Ext JS 3, revisa todos los tutoriales que tenemos en este mismo curso ya que están en secuencia y van de lo más sencillo a lo más complicado.

                        Tendrás acceso a descargar los videos, códigos y material adicional.

                        Podrás resolver los ejercicios incluidos en el curso así como los Quizzes.

                        Llevarás un registro de tu avance.