Extensión de los dijits de Dojo para crear widgets personalizados

Este artículo muestra lo que puede hacer cuando un dijit particular del kit de herramientas de Dojo no atiende por completo sus requisitos y su necesidad de crear su propio widget personalizado. Al final, utilizando un ejemplo con un conjunto de requisitos y un enfoque para la forma de cumplirlos, estará familiarizado con el uso de un dijit y otras funcionalidades centrales de Dojo y con cómo declarar su propio widget.

Kareem Weller, Staff Software Engineer,, IBM

Kareem Weller es un ingeniero de software de personal en IBM y actualmente forma parte de IBM Software Group; trabaja desde Orlando, Florida. Ha trabajado en varias organizaciones dentro de IBM y tiene 5 años de experiencia en el desarrollo de aplicaciones web, también ha trabajado en muchos proyectos gubernamentales y comerciales utilizando distintas tecnologías y productos web tales como el kit de herramientas de dojo, JSON, XML, IBM Web Content Management y Websphere Application Server.



05-02-2013

Introducción

El Kit de Herramientas de Dojo es una poderosa biblioteca de JavaScript™ que permite a los desarrolladores crear Aplicaciones Enriquecidas de Internet utilizando widgets orientados a objetos con mínimo tiempo y esfuerzo de desarrollo. Viene con cuatro paquetes, conocidos como Dojo (el núcleo), Dijit (la infraestructura de IU), dojox (la extensión de dojo) y util. Puede utilizar la funcionalidad proporcionada por el kit de herramientas como está, o puede extenderlas y crear sus propios widgets. Las funcionalidades proporcionadas incluyen manipulación de DOM, desarrollo con AJAX, eventos, depósitos de datos y más.

El paquete de Dijit (widget dojo), la propia biblioteca de dojo, contiene una colección de clases de dojo que permiten a los desarrolladores crear interfaces de Web 2.0 enriquecidas y poderosas con esfuerzo mínimo. Estos widgets de Dijit, o dijits, están soportados por temas que son fáciles de manipular. Ejemplos de los dijits en este paquete son botones, campos de texto, editores, barras de progreso y mucho más.

Utilizando estos dijits, por ejemplo, puede crear un formulario de envío que incluya campos de texto para nombre, dirección de email y números telefónicos, además de campos de fecha, botones y validación, todo en cuestión de minutos y con un mínimo conocimiento de JavaScript.

Uno de los dijits más ricos proporcionado es el dijit Calendar, el cual le permite mostrar un calendario en el contexto de un mes. Los usuarios pueden navegar fácilmente mes por mes, año por año o saltarse a cualquier mes en el mismo año para seleccionar fechas específicas.

Al trabajar en el desarrollo de una Aplicación Enriquecida de Internet (RIA), frecuentemente puede utilizar los dijits como están. Sin embargo, algunas veces puede requerir un estilo distinto (como cambiar el color o tema), o cambios más involucrados que puedan requerir una combinación de funcionalidad, plantilla y cambios de estilo. Puede cumplir estos requisitos al crear un nuevo widget personalizado desde cero o crear un widget personalizado que extienda un dijit existente.

Este artículo presenta un ejercicio en el cual usted tiene un requisito para utilizar una variación distinta del widget Calendar en su sitio web. Para cumplir con este requisito, creará una nueva clase que podrá satisfacer los requisitos. Este ejercicio utiliza Dojo versión 1.7 y ofrece una oportunidad para explorar el dijit Calendar y formas de reutilizar un dijit existente con modificaciones mínimas para salvar al momento del desarrollo. También verá un ejemplo funcional de una nueva clase declarada en Dojo 1.7 y explorará algunas de las funciones base de Dojo, como la manipulación de fechas, enganchado, publicar y suscribir y más.

El problema

En este ejercicio, trabajará con una versión personalizada del dijit Calendar con estos requisitos:

  • Calendar solo debe mostrar los días del mes en curso (ocultar e inhabilitar días que no pertenecen a ese mes).
  • Calendar solo debe mostrar el año en curso (sin años anteriores o siguientes) al fondo del calendario.
  • Calendar debe mostrar el nombre del mes actual en la parte superior del widget de calendario.
  • Los usuarios no pueden saltar a cualquier otro mes (inhabilitar el botón desplegable del mes en la parte superior).
  • Extraer las flechas mostrada en la parte superior del calendario para moverse de mes en mes (hacia atrás o hacia adelante) y mostrar flechas en su lugar junto al calendario como botones de dijit. Estos dos nuevos botones son la única forma para que el usuario cambie el mes.
  • Existen fechas límite mínimas y máximas, lo que significa que todas las fechas fuera de estos límites deben estar inhabilitadas y ser inaccesibles.
  • Inhabilitar el botón de navegación de mes apropiado cuando una fecha límite es alcanzada.
  • Añadir un estilo especial a los días seleccionados en el calendario.
  • Cuando un usuario selecciona una fecha, pasar la fecha a una función que procesará el nuevo valor seleccionado.

La solución es crear un widget personalizado desarrollado al editar el dijit Calendar utilizando JavaScript y CSS. La Figura 1 muestra el widget Calendar antes (izquierda) y después (derecha) de que los requisitos anteriores son aplicados.

Figura 1. Figura 1. Comparación de un dijit Calendar estándar con un widget Calendar personalizado
Figure 1. Comparison of a standard Calendar dijit to custom Calendar widget

Para hacer esto, necesitará crear tres archivos:

  • Plantilla de dijit: Una marcación que mostrará los componentes del widget personalizado.
  • Clase de dijit: Una clase de widget creada utilizando declaración (JavaScript).
  • Archivo CSS: Contiene todas las clases de hoja de estilo necesarias.

La Figura 2 muestra la estructura y ubicación del archivo de su widget personalizado. Su punto de partida es index.html, que actuará como el controlador para el widget en este ejemplo. El archivo simple.css contendrá todos los estilos.

Figura 2. Figura 2. Estructura del archivo
Figure 2. File structure

Creando el widget

La plantilla de dijit

Cree tres elementos div de JavaScript: uno para el calendario y dos para los botones de flecha para navegar de mes a mes hacia atrás y hacia adelante (Listado 1). Los divs utilizarán puntos de adición (punto de adición de datos de dojo) como referencia. Utilizar puntos de adición es una mejor opción que utilizar ids, ya que esto le permitirá tener múltiples instancias del mismo widget en la misma página sin tener que preocuparse por conflictos de id.

Listado 1. Listado 1. La plantilla de dijit
<div class="CalendarArrow">
<div data-dojo-attach-point="calendarPreviousMonthButtonAP"></div>
</div>

<div class="CalendarDijit">
<span data-dojo-attach-point="calendarMonthOneAttachPoint"></span>
</div>

<div class="CalendarArrow">
<div data-dojo-attach-point="calendarFollowingMonthButtonAP"></div>
</div>

La clase de dijit

Según los requisitos de aplicación, es necesario definir estas variables:

  • selectedDate: Valor inicial para el calendario.
  • currentFocusDate: El valor que el calendario toma como referencia para saber qué mes mostrar; inicialmente establece el equivalente a selectedDate.
  • calendarInstance: Instancia de calendario de dijit.
  • bookingWindowMaxDate: El último día permitido en el calendario.
  • bookingWindowMinDate: Primer día permitido en el calendario.
  • onValueSelectedPublishIDString: Cadena que representa el canal (o tema) pub/sub.

Las funciones de JavaScript

Comience por modificar estos elementos de hoja de estilo:

  • constructor

    Altere temporalmente el constructor para copiar las variables pasadas desde el controlador. Utilice dojo/_base/lang/mixin, lo que coincidirá los nombres de variable y copiará los valores en sus variables de widget personalizado (Listado 2).

    Listado 2. Listado 2. El constructor
    constructor: function (args){
    	if(args){
    		lang.mixin(this,args);
    	}
    }
  • postCreate

    Todas las fechas serán pasadas como una cadena en el formato corto en-us de mm/dd/yyyy. Convierta todas las cadenas de datos en objetos de fecha utilizando la función dojo/date/locale para selectedDate, bookingWindowMaxDate, bookingWindowMinDate (Listado 3).

    Listado 3. Listado 3. Utilizando dojo/date/locale
    this.bookingWindowMinDate = locale.parse(this.bookingWindowMinDate, {formatLength:
    'short', selector:'date', locale:'en-us'});

    Cree una instancia del objeto de calendario (Listado 4). La lógica para la creación está en la función createCalendar. Usted crea una instancia de un calendario de dijit programáticamente y la adjunta a un div que creará utilizando dojo/dom-construct (equivalente a dojo.create en una versión anterior de dojo). Esta es una buena práctica en general, ya que le permite destruir el calendario sin perder el punto de adición.

    Listado 4. Listado 4. Retornando una instancia de dijit/Calendar
    return new Calendar({
    	value : selectedDate,
    	currentFocus : selectedDate	},  domConstruct.create("div", {}, 
    	calendarAttachPoint));
    }

    Note que está estableciendo el valor de currentFocus en el dijit de calendario. El dijit Calendar siempre utilizará su fecha local actual para mostrar su primera pantalla de aterrizaje, así que si desea que el calendario muestre una pantalla (fecha) distinta, tiene que establecer currentFocus. Por lo tanto, para su widget personalizado necesita establecer el valor inicial para el calendario y currentFocus como selectedDate (por requisitos). Para este ejemplo, ese es un día en agosto de 2012.

Para satisfacer los otros requisitos, necesita alterar temporalmente estas tres funciones del dijit Calendar:

  • isDisabledDate

    Cuando el dijit de calendario está cargando una vista, itera mediante los días de la vista actual uno por uno (los 42 días) y llamará a las funciones isDisabledDate y getClassForDate (texto cubierto) para cada día.

    La función isDisabledDate es utilizada para inhabilitar ciertas fechas en el calendario (Listado 5). Si la función retorna true, el día será inhabilitado. Cada vez que el calendario se renueva, esta función es llamada y cada día en el calendario es pasado a ella. Para su widget personalizado, es necesario:

    • Inhabilitar cualquier día que no pertenezca al mes actual: Para hacer esto, utilizará la función dojo/date/difference, la cual compara dos objetos de fecha con base en un intervalo y retorna 0 si es igual. Comparará la variable currentFocusDate con cada día en la vista actual utilizando el intervalo de mes y retornará true si no son iguales para inhabilitar el día.
    • Inhabilite los días fuera de las fechas límite: Use dojo/date/difference de nuevo, pero con el intervalo establecido como "day". Si el valor de retorno es más pequeño que bookingWindowMinDate o más grande bookingWindowMaxDate, entonces retorna true para inhabilitar la fecha.
      Listado 5. Listado 5. Alterando temporalmente a isDisabledDate
      isDisabledDate: function(date) {
       //disable any day that doesn't belong to current month
      	if(dojoDate.difference(parent.currentFocusDate, date, "month")!==0){
      		return true;
      	}
      	if(dojoDate.difference(parent.bookingWindowMinDate, date, "day" || 
      dojoDate.difference(parent.bookingWindowMaxDate, date, "day")<0){
      		return true;
      	}
      	else {
      		return false;
      	}
      }
  • getClassForDate

    Aunque inhabilitó los días que no pertenecen a la vista del mes en curso con isDisabledDate, necesita ocultarlos también. La función getClassForDate es utilizada para retornar un nombre de clase de CSS para marcar el día de forma distinta en el calendario. Para su widget personalizado, necesita indicar el selectedDate al añadir un recuadro azul con un marco negro para esa fecha (Listado 6). También necesita indicar las fechas fuera de sus límites mínimos y máximos con un color gris y ocultar los días que no pertenecen al mes en curso.

    Para identificar la fecha que necesita tener un estilo distinto, puede utilizar dojo/date/compare, lo cual tomará dos valores de fecha (objetos de fecha) y porción (cadena) y retornará 0 si es igual. Aquí, pasará currentFocusDate, el día en iteración y "date" para porción, ya que solo está interesado en comparar la fecha sin la indicación de fecha y hora. Si esta comparación retorna 0, esta función retornará la clase "Available", que es definida en su archivo de CSS (Listado 7). Estará utilizando los selectores .class de CSS para dirigirse a los elementos específicos que deseamos cambiar.

    Listado 6. Listado 6. Alterando temporalmente getClassForDate
    getClassForDate: function(date) {	
    	if ( dojoDate.compare(date,selectedDate,"date") === 0) {
    		return "Available";
    	} // apply special style
    }
    Listado 7. Listado 7. Clase de CSS para marcar los días disponibles
    .AvailabilityCalendars .Calendars .CalendarDijit .Available 
    	.dijitCalendarDateLabel
    {
            background-color: #bccedc !important;
            border: 1px solid #000000 !important;
    }

    Utilizará lo mismo si las condiciones de isDisabledDate para identificar los días fuera de los límites y los días que no pertenecen al mes en curso, pero retornará el nombre de clase de CSS (Listados 8 y 9).

    Listado 8. Listado 8. Ocultando e inhabilitando días
    if(dojoDate.difference(parent.currentFocusDate, date, "month")!==0){ 
    	return "HiddenDay";
    }
    if(dojoDate.difference(parent.bookingWindowMinDate, date, "day")<0 || 
    	dojoDate.difference(parent.bookingWindowMaxDate, date, "day")>0){
    	return "Disabled";
    }
    Listado 9. Listado 9. Clase de CSS para marcar días ocultos e inhabilitados
     .AvailabilityCalendars .Calendars .CalendarDijit .HiddenDay 
    	.dijitCalendarDateLabel
    {
        background-color: #ffffff !important;
        border-color: #ffffff;
        color: #ffffff;
    }
    
     .AvailabilityCalendars .Calendars .CalendarDijit .Disabled 
    	.dijitCalendarDateLabel
    {
    	background-color: #9c9c9c;
    }
  • onChange

    Esta función es invocada solo cuando establece un nuevo valor en el calendario o cuando selecciona un día habilitado en el calendario (Listado 10). Esta función retorna un objeto de fecha del día seleccionado. Utilizará eso para publicar la fecha para otro método que procesará la fecha. Llame una función definida en su widget personalizado (onValueSelected) donde pueda hacer cualquier procesamiento necesario antes de publicar en el controlador (Listado 11). En este ejemplo, solo publicará la fecha para el controlador utilizando dojo/_base/connect/publish. El la cadena de canal (o tema) es almacenada en la variable onValueSelectedPublishIDString.

    Listado 10. Listado 10. Utilizando onChange y hitch de Calendar
    onChange : lang.hitch(this, function(date){
    	this.onValueSelected(date);
    })
    Listado 11. Listado 11. Utilizando publish
    onValueSelected : function (date){
    	connect.publish(this.onValueSelectedPublishIDString, [date]);
    }

    Note que utilizó dojo/_base/lang/hitch para dar el ámbito para llamar la función onValueSelected (Listado 10). Su controlador (en este escenario, index.html) tendrá un suscriptor para ese canal para procesar la fecha (Listado 12). En este ejemplo, usted simplemente lo registra. Puede sustituir esto con cualquier otra lógica necesaria.

    Listado 12. Listado 12. Suscriptor para nuestra publicación
    connect.subscribe("selectedValueID", function(date){
      //Do some processing 
      console.log("New Selected Date: ", date);
    });

El dijit Calendar se envía con un monthDropDownButton en la cabecera. Este botón muestra una lista de todos los meses y permite al usuario saltar a cualquier mes. Para cumplir con los requisitos, necesita inhabilitar este botón al establecer monthWidget como "disabled" (Listado 13).

Listado 13. Listado 13. Inhabilitar el botón desplegable en la cabecera de Calendar
this.calendarInstance.monthWidget.set("disabled", true);

Desde una perspectiva de usabilidad, también necesita ocultar la flecha, de forma que el usuario no se sienta tentado a hacer clic en ella. Para hacer eso, añada clases de CSS que se dirigirán a los elementos que desea manipular (Listado 14).

Listado 14. Listado 14. Clase de CSS para ocultar la flecha del botón desplegable
 .AvailabilityCalendars .Calendars .CalendarDijit .dijitDropDownButton 
	.dijitArrowButtonInner
{
    visibility: hidden;
}

A continuación, use clases de CSS para ocultar los dígitos de los años anteriores y posteriores y evitar que sean mostrados en el fondo (Listado 15).

Listado 15. Listado 15. Clase de CSS para ocultar los dígitos de los años al fondo de Calendar
 .AvailabilityCalendars .Calendars .CalendarDijit .dijitCalendarPreviousYear, 
	.dijitCalendarNextYear
    {
padding: 1px 6px;
visibility: hidden;
    }

También ocultará las flechas en la parte superior, lo cual de otra forma habilitaría a un usuario para moverse de mes a mes (Listado 16).

Listado 16. Listado 16. Clase de CSS para ocultar la flecha de los meses en la parte superior de Calendar
 .AvailabilityCalendars .Calendars .CalendarDijit .dijitCalendarArrow
{
     visibility: hidden;
}

A continuación, cree los dos nuevos botones para permitir al usuario navegar a través de los meses. Use dijit/form/Button y créelos programáticamente. Para el primer botón (de retroceso), establezca la etiqueta como "<<" y altere temporalmente la función onClick (Listado 17). La lógica para onClick estará en la función goToPreviousMonth.

Listado 17. Listado 17. Creando una instancia de un dijit/form/bottom
this.calendarPreviousMonthButton = new Button({
       label: "<<",	
       onClick: lang.hitch(this, function(){
    	   this.goToPreviousMonth(this.calendarInstance);
       })
}, this.calendarPreviousMonthButtonAP);

Usted desea que el calendario se mueva hacia atrás un mes cada vez que un usuario hace clic en el botón. En goToPreviousMonth, necesita primero cambiar currentFocusDate a currentFocusDate - 1 mes y después renovar la vista del calendario. Finalmente, debe verificar si este es el último mes a mostrar y, si es así, inhabilite el botón.

Use la función dojo/date/add, la cual toma un objeto de fecha, un intervalo (Cadena) y una cantidad (entero). Para su situación, la fecha será el objeto currentFocusDate, el intervalo es "month" y la cantidad es -1 (Listado 18).

Listado 18. Listado 18. Vista de calendario retrocediendo un mes
this.currentFocusDate = dojoDate.add(this.currentFocusDate,"month",-1);
calendarInstance.set("currentFocus",this.currentFocusDate);

Establezca la nueva vista para el calendario al establecer currentFocus con el nuevo valor de fecha. (Esto renovará automáticamente el calendario y mostrará la nueva vista).

Finalmente, verifique si esta será la última vista de mes al comparar currentFocusDate con el límite mínimo; si es así, entonces inhabilite el botón de retroceso. También, verifique si debe habilitar el botón de avance (en caso de que estuviera inhabilitado y ahora se esté alejando del límite máximo) (Listado 19).

Listado 19. Listado 19. Verificar si debemos inhabilitar los nuevos botones de navegación
if(this.isLastCalendarMonth(this.bookingWindowMinDate, this.currentFocusDate)){
	this.calendarPreviousMonthButton.set("disabled", true);
}
if(!this.isLastCalendarMonth(this.bookingWindowMaxDate, this.currentFocusDate)){
	this.calendarFollowingMonthButton.set("disabled", false);
}

El segundo botón funciona de la misma manera. La etiqueta será ">>" y onClick llama a goToNextMonth, que usa la misma función, excepto que usted añade un mes (Listado 20).

Listado 20. Listado 20. Función que controla el botón para moverse al siguiente mes
goToNextMonth : function (calendarInstance){
	this.currentFocusDate = dojoDate.add(this.currentFocusDate,"month",1);
	calendarInstance.set("currentFocus",this.currentFocusDate);
	if(this.isLastCalendarMonth(this.bookingWindowMaxDate, this.currentFocusDate)){
		this.calendarFollowingMonthButton.set("disabled", true);
	}	
	if(!this.isLastCalendarMonth(this.bookingWindowMinDate, this.currentFocusDate)){
		this.calendarPreviousMonthButton.set("disabled", false);
	}
}

Finalmente, el Listado 21 muestra un ejemplo de lo que estaría en su clase de controlador, la cual hace la llamada para instanciar su nuevo customCalendar.

Listado 21. Listado 21. Instantánea de cómo crear una instancia del nuevo widget de calendario personalizado
require(["myUtil/customCalendar","dojo/_base/connect"], function(myCalendar, connect){
	var params = {
		"bookingWindowMinDate":"10/9/2011",
		"bookingWindowMaxDate":"10/9/2012",
		"selectedDate":"8/15/2012",
		"onValueSelectedPublishIDString":"selectedValueID"
	};
	var myTest = myCalendar(params);
	myTest.placeAt("nodeId", "last");
		
	connect.subscribe("selectedValueID", function(date){
	  //Do some processing 
	  console.log("I got: ", date);
	});	
	
});

Como puede ver, está creando los parámetros de un objeto con los valores necesarios para pasar a su widget de calendario personalizado y suscribirse al canal.

Más funciones

Existen algunas otras funciones y propiedades que puede encontrar útiles en ese escenario:

  • Cuando pasa un objeto de fecha y un local a dojo/date/locale/isWeekend, retorna true si el día es de fin de semana (sábado y domingo para local en-us). Esto puede ser utilizado para inhabilitar o dar un estilo distinto a los días del fin de semana, si es necesario.
  • el dijit Calendar también contiene una propiedad dayWidth que toma una cadena como un valor. De forma predeterminada está configurada como "narrow", lo cual hace más corta la visualización del día de calendario, por ejemplo, utilizando "M" en lugar de "Monday". Otros valores son "wide" para visualización del nombre del día completo y "abbr" para una abreviación (como "Mon").

Una variación de estos requisitos de widget personalizado pudo haber pedido que el widget muestre múltiples calendarios y después requerir que ambos calendarios avancen cuando el usuario hace clic para ver el siguiente mes (Figura 3). Esto puede ser fácilmente logrado al cambiar las variables de widget para soportar una matriz y no una sola variable de un solo valor.

Figura 3. Figura 3. Múltiples calendarios por vista
Figure 3. Multiple calendars per view

Conclusión

Mediante una combinación de JavaScript y modificaciones de CSS, puede crear fácilmente un widget personalizado para satisfacer mejor sus requisitos de proyecto. Este artículo demostró esta práctica utilizando Dojo 1.7 para declarar una clase que extendió el dijit Calendar y exploró algunas de las funcionalidades de dojo como manipulación de fechas, enganchado, publicar y suscribir y otras funciones básicas de dojo. Con suerte, podrá aplicar estas etapas para ampliar el dijit de Dojo y crear nuevos widgets de su propia autoría.


Descargar

DescripciónNombretamaño
Sample applicationcustomCalendar17.zip4 KB

Recursos

Aprender

Obtener los productos y tecnologías

Comentarios

developerWorks: Ingrese

Los campos obligatorios están marcados con un asterisco (*).


¿Necesita un IBM ID?
¿Olvidó su IBM ID?


¿Olvidó su Password?
Cambie su Password

Al hacer clic en Enviar, usted está de acuerdo con los términos y condiciones de developerWorks.

 


La primera vez que inicie sesión en developerWorks, se creará un perfil para usted. La información en su propio perfil (nombre, país/región y nombre de la empresa) se muestra al público y acompañará a cualquier contenido que publique, a menos que opte por la opción de ocultar el nombre de su empresa. Puede actualizar su cuenta de IBM en cualquier momento.

Toda la información enviada es segura.

Elija su nombre para mostrar



La primera vez que inicia sesión en developerWorks se crea un perfil para usted, teniendo que elegir un nombre para mostrar en el mismo. Este nombre acompañará el contenido que usted publique en developerWorks.

Por favor elija un nombre de 3 - 31 caracteres. Su nombre de usuario debe ser único en la comunidad developerWorks y debe ser distinto a su dirección de email por motivos de privacidad.

Los campos obligatorios están marcados con un asterisco (*).

(Por favor elija un nombre de 3 - 31 caracteres.)

Al hacer clic en Enviar, usted está de acuerdo con los términos y condiciones de developerWorks.

 


Toda la información enviada es segura.


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=90
Zone=WebSphere, Desarrollo móvil
ArticleID=857168
ArticleTitle=Extensión de los dijits de Dojo para crear widgets personalizados
publish-date=02052013