El otro día me plantearon esta cuestión ¿Cómo puedo hacer que los valores de un picklist (lista desplegable) varíen en función de los valores de otro picklist? Al recibirla me puse a pensar, y recordé que en alguna parte había visto un ejemplo de cómo conseguir esto. Así que me puse a buscar donde lo había visto para responder a la pregunta. Después de un rato buscando, encontré esta entrada del blog de Ben Vollmer donde explicaba cómo conseguirlo.
La solución que proponía Ben Vollmer está muy bien, de hecho, me parece una de las formas más elegantes de conseguirlo. Pero al probarla descubrí un pequeño problema, nada grave, que consistía en que al abrir un registro en modo de actualización un registro se perdía la selección hecha en la lista dinámica. Así que basándome en la idea y el código de Ben Vollmer he creado un ejemplo sobre cómo conseguir picklist dinámicos solucionando ese pequeño problemilla.
¿Qué es un picklist dinámico? Básicamente es hacer que las opciones que se presentan en un picklist varíen de forma dinámica según alguna opción que elija un usuario (otro picklist, checkbox, etc…). Por ejemplo, tenemos un picklist donde se puede elegir el sector al que pertenece una cuenta y otro en el que podemos elegir el subsector. La idea es que al elegir un sector en el picklist de subsectores solo se muestren los que correspondan a ese sector. En este ejemplo haremos esto, es decir, que varíen las opciones de un picklist en función de otro picklist, pero la idea sería la misma en otras situaciones.


Antes de seguir, decir que al final del post podéis encontrar un enlace para descargaros la demos (código y personalizaciones).
Para construir nuestro ejemplo vamos a crear una entidad nueva en el CRM llamada "Demo Picklist", y le vamos añadir dos campos de tipo picklist: uno llamado "lista" y otro "sublista". Como es evidente, el objetivo es que las opciones que se muestran en la sublista varíen según la opción seleccionada en la lista. Al crear los picklist debemos indicar todas las opciones disponibles en la lista y en la sublista, pero es muy importante que las de la sublista vayan en orden con respecto a las de la lista, tal y como vemos en la siguiente tabla.
| Lista |
Sublista |
| Nº Opción |
Texto |
Nº Opción |
Texto |
| 1 |
Elemento 1 |
1 |
Elemento 1-1 |
| 2 |
Elemento 1-2 |
| 3 |
Elemento 1-3 |
| 2 |
Elemento 2 |
4 |
Elemento 2-1 |
| 5 |
Elemento 2-2 |
| 6 |
Elemento 2-3 |
| 3 |
Elemento 3 |
7 |
Elemento 3-1 |
| 8 |
Elemento 3-2 |
Como se ve en la tabla, por cada opción de la lista tenemos una serie de opciones correspondientes en la sublista. Es muy importante que las opciones de la sublista vayan agrupadas (ordenadas) en función de la opción de la lista a la que pertenecen para facilitar el trabajo posterior de filtrar opciones a mostrar en la sublista.
Bien, ya tenemos creados los picklist y los hemos añadido al formulario ¿Qué código JScript necesitamos para hacer funcionar el invento? Lo primero será añadir código al onload() del formulario para poder hacer las inicializaciones necesarias. En este código debemos de hacer una copia del conjunto completo de opciones de la sublista para poder recuperar en cada momento las que necesitemos, para ello copiamos el valor de la propiedad Options en la propiedad originalPicklistOptions, así podremos recuperarlas cuando nos hagan falta. Además, si ya tenemos seleccionado un valor en la lista debemos de limitar el conjunto de opciones de la sublista (llamando al onchange de la lista, como veremos más adelante) y si no deshabilitarla. Básicamente es lo que hace el siguiente código.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
var CRM_FORM_TYPE_CREATE = 1; var CRM_FORM_TYPE_UPDATE = 2;
// Solo habilitamos el picklist dinamico para formularios de creacion o // actualizacion, para el resto no es necesario. switch (crmForm.FormType) { case CRM_FORM_TYPE_CREATE: case CRM_FORM_TYPE_UPDATE:
// Obtener referencia a la lista maestra var lista = crmForm.all.new_lista; // Obtener referencia a la sub lista var sublista = crmForm.all.new_sublista;
// Guardamos el conjunto completo original de opciones de la sublista // utilizando la propiedad originalPicklistOptions sublista.originalPicklistOptions = sublista.Options;
// Si no hay ningun valor seleccionado en la lista // deshabilitamos la sublista. Se volvera a habilitar // cuando el usuario seleccione un valor if (lista.DataValue == null) { sublista.Disabled = true; } else { // Ya tenemos seleccionado un valor, debemos limitar el conjunto // de iopciones de la sublista. Para ello disparamos el evento // onchange de la lista.
// Almacenamos el valor seleccionado en la sublista var temp = sublista.DataValue; // Disparamos el onchange para limitar las opciones new_lista_onchange0(); // Volvemos a fijar la opcion que estaba seleccionada sublista.DataValue=temp; }
break; } |
Nota: El problema, que tenía el código de Ben Vollmer era que le faltaban las líneas 29 y 35, lo que hacía que cuando estábamos en un formulario de actualización se perdiese la opción seleccionada en la sublista.
Ahora tenemos que escribir el código para el onchage de lista. Este código se encargará de que cada vez que se modifique el valor seleccionado en la lista se establezcan las posibles opciones en la sublista. Gracias a que introducimos en orden los valores de la sublista, lo que tenemos que hacer es elegir un subarray del array original de opciones de la sublista (que en el onload convenientemente copiamos a la propiedad originalPicklistValue) basándonos en la opción seleccionada en la lista. Por ejemplo, para la opción 1 de la lista el array sería del elemento 1 al 3. Y una vez que tenemos el array no hay más que asignárselo al array Options de la sublista. ¡Y listo¡ Ya tenemos picklist dinámico. El código es el siguiente.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 |
// En funcion de el elemento seleccionado en la lista, escogeremos las opciones que se muestran // en la sublista. var lista = crmForm.all.new_lista; var sublista = crmForm.all.new_sublista; var comienzo = -1; var fin = -1;
// Para facilitar la tarea las opciones de la sublista estan agrupadas en orden de correspondencia // con las opciones de la lista, así que solo tenemos que elegir un rango de opciones de la // sublista en función de las opcion de la lista switch (lista.DataValue) { case "1": comienzo = 1; fin = 3; break;
case "2": comienzo = 4; fin = 6; break; case "3": comienzo = 7; fin = 8; break; }
// Si tenemos indices limitamos las opciones de la sublista if (comienzo > -1 && fin > -1) { // Creamos un array temporal donde almacenar las opciones var tempArray = new Array();
// Inicializar indice para el array temporal var indice = 0;
// Ahora recorremos la lista original de opciones de la sublista que // cacheamos en el onload del formulario y recogemos las que // perteneces a la opcion seleccionada en la lista for (var i = comienzo; i <= fin; i++) { tempArray[indice] = sublista.originalPicklistOptions ; indice++; }
// Copiamos el subconjunto de opciones seleccionado sublista.Options = tempArray;
// Habilitamos la sublista sublista.Disabled = false; } else { // No se ha seleccionado una opción en la lista, o la opcion no es valida sublista.DataValue = null; sublista.Disabled = true; } |
Para facilitar la labor, os dejo un zip con el código, y un fichero xml con la personalización de prueba para que podáis importarla en vuestro sistema y ver el funcionamiento. Y ahora os digo lo típico (es que me hace ilusión ponerlo): Este código se proporciona tal cual, sin garantía de ningún tipo. No es recomendable utilizarlo en un sistema en producción sin haberlo probado primero. Y leed el leeme.doc.
¿Qué os parecen los picklist dinámicos? ¿Algún bug en el código? Espero vuestras opiniones, correcciones, etc..
Un saludo,
Marco Amoedo