Mostrando las entradas con la etiqueta vaadin en español. Mostrar todas las entradas
Mostrando las entradas con la etiqueta vaadin en español. Mostrar todas las entradas






El Libro de Vaadin
4ta Edicion



El Libro de Vaadin es la referencia completa de Vaadin. Le muestra cómo empezar, proporciona un buen compendio de las características del framework.



Siguiente
Prólogo

5.2. Interfaces y Abstracciones

Los componentes de la interfaz de usuario de Vaadin se basan en un esqueleto de interfaces y clases abstractas que definen e implementan las características comunes de todos los componentes y la lógica básica de cómo son serializados los estados del componente entre el servidor y el cliente.

Esta sección proporciona detalles sobre las interfaces de los componentes básicos y abstracciones. El diseño y otras abstracciones del contenedor de componentes se describen en el Capítulo 6, Administrar el Diseño. Las interfaces que definen el modelo de datos de Vaadin se describen en el Capítulo 9, Vincular Componentes a Datos.

Figura 5.2. Interfaces de Componentes y Abstracciones

Todos los componentes implementan la interfaz Paintable, que se utiliza para serializar ("pintando") los componentes para el cliente, y la interfaz inversa VariableOwner, que es necesaria para des serializar el estado del componente o la interacción del usuario desde el cliente.

Además de las interfaces definidas en el framework Vaadin, todos los componentes implementan la interfaz java.io.Serializable para permitir la serialización. La serialización es necesaria en muchas agrupaciones y soluciones de computación en la nube (cloud computing).

5.2.1 Interfaz Component

La interfaz Component se combina con la clase AbstractComponent, que implementa todos los métodos definidos en la interfaz.

Administración del Árbol de Componentes
Los componentes son presentados en la interfaz de usuario jerárquicamente. El diseño es administrado por los componentes de diseño, o más en general por los componentes que implementan la interfaz ComponentContainer. Este contenedor es el padre de los componentes contenidos.

El método getParent() permite recuperar el componente padre de un componente. Si bien existe un setParent(), que rara vez se necesita cuando por lo general agrega componentes con el método addComponent() de la interfaz ComponentContainer, el cual establece automáticamente al padre.

Un componente no conoce a su padre cuando el componente es creado, por lo que no puede referirse al padre en el constructor con getParent(). Además, no es posible traer una referencia al objeto application con getApplication() antes de tener un padre. Por ejemplo, lo siguiente es inválido:

public class EjemploAcoplar extends CustomComponent {
    public EjemploAcoplar() {
        // ERROR: No podemos tener acceso al objeto de application aún.
        ClassResource r = new ClassResource("smiley.jpg",
                                            getApplication());
        Embedded image = new Embedded("Image:", r); 
        setCompositionRoot(image);
    }
}

La adición de un componente a una aplicación provoca que se active el llamado del método attach() para el componente. En consecuencia, la eliminación de un componente de un contenedor activa el llamado al método detach(). Si el padre de un componente agregado ya está conectado a la aplicación, attach() es llamado inmediatamente desde setParent().

public class EjemploAcoplar extends CustomComponent {
    public EjemploAcoplar() {
    }
    
    @Override
    public void attach() {
        super.attach(); // Debe llamar.
        
        // Ahora sabemos quién es finalmente nuestro dueño.
        ClassResource r = new ClassResource("sonriente.jpg",
                                            getApplication());
        Embedded imagen = new Embedded("Imagen:", r); 
        setCompositionRoot(imagen);
    }
}

La lógica del acoplamiento es implementado en AbstractComponent, tal como se describe en la Sección 5.2.2, "AbstractComponent".

5.2.2 AbstractComponent

AbstractComponent es la clase base para todos los componentes de interfaz de usuario. Se trata (sólo) de la implementación de la interfaz Component, implementando todos los métodos definidos en la interfaz.

AbstractComponent tiene un único método abstracto, getTag(), el cual devuelve el identificador de la serialización de la clase de un componente en particular. Este tiene que ser implementado cuando (y sólo cuando) se creen componentes totalmente nuevos. AbstractComponent administra gran parte de la serialización de los estados de los componentes entre el cliente y el servidor. La creación de nuevos componentes y la serialización se describe en el Capítulo 11, Desarrollar Nuevos Componentes, y la API de serialización del lado del servidor en el Apéndice A, Definicion del Lenguaje de Interfaz de Usuario (UIDL).

Componentes Campo (Field y AbstractField)

Los Campos son los componentes que tienen un valor que el usuario puede cambiar a través de la interfaz de usuario. La Figura 5.3, "Componentes Campo", ilustra las relaciones de herencia y las interfaces importantes y las clases base.

Figura 5.3. Componentes Campo

Los componentes campo se basan en el framework definido en la interfaz Field y de la clase base AbstractField.

Los campos están fuertemente acoplados con el modelo de datos de Vaadin. El valor del campo es tratado como una Property del componente field. Los campos de selección permiten la administración de elementos seleccionables a través de la interfaz Container.

La descripción de las interfaces field y las clases base se divide en las siguientes secciones.

Interfaz Field
La interfaz Field hereda la superinterface Component y también la interfaz Property para tener un valor para el campo. AbstractField es la única clase que implementa directamente la interfaz Field. Las relaciones se ilustran en la Figura 5.4, "Diagrama de Herencia de la Interfaz Field".

Figura 5.4. Diagrama de Herencia de la Interfaz Field

Puede establecer el valor del field con setValue() y leerlo con el método getValue() definido en la interfaz Property. El tipo de valor actual depende del componente.

La interfaz Field define una serie de atributos, que se pueden recuperar o manipular con el setters y getters correspondiente.
  • description
    Todos los campos tienen una descripción. Tenga en cuenta que si bien, este atributo se define en el componente Field, el cual es implementado en AbstractField, que no implementa directamente a Field, pero sólo a través de la clase AbstractField.

    required
    Cuando está activado, un indicador de (por lo general el carácter * asterisco) es mostrado a la izquierda, arriba, o hacia la derecha del campo, dependiendo del diseño que lo contiene y si el campo tiene un título. Si estos campos son validados pero están vacíos y se activa la propiedad requiredError (ver más abajo), se muestra un indicador de error y el error del componente se establece en el texto definido con la propiedad error. Sin la validación, el indicador requerido no es más que una guía visual.

    requiredError
    Define el mensaje de error a mostrar cuando se requiere un valor para un campo, pero no se ingresa nada. El mensaje de error se establece como el error del componente para el campo y por lo general se muestra en un texto de ayuda cuando el puntero del mouse se desplaza sobre el indicador de error. El componente Form puede mostrar el mensaje de error en un área indicadora de error especial.
Controlar Cambios de Valor en Field
Field hereda a Property.ValueChangeListener para permitir escuchar los cambios de valor en los campos y Property.Editor para permitir editar los valores.

Cuando el valor de un campo cambia, es desencadenado un Property.ValueChangeEvent para el campo. No debería implementar el método valueChange() en una clase que herede a AbstractField, ya que se ha implementado en AbstractField. En su lugar, debe implementar el método explícitamente añadiendo la implementación del objeto como un oyente.

Clase Base AbstractField
AbstractField es la clase base para todos los componentes field. Además de las características de los componentes heredadas de AbstractComponent, esta implementa varias características definidas en las interfaces Property, Buffered, Validatable, y Component.Focusable.



Anterior
Capítulo 5. Componentes de Interfaz de Usuario
Siguiente
5.3. Características Comunes de los Componentes

Capítulo 5. Componentes de Interfaz de Usuario

Este capítulo proporciona una información general y una descripción detallada de todos los componentes de no-diseño en Vaadin.

5.1 Información General

Vaadin proporciona un conjunto completo de componentes de interfaz de usuario y le permite definir componentes personalizados. La Figura 5.1, "Diagrama de Herencia de los Componentes de Interfaz de Usuario" ilustra la jerarquía de herencia de las clases de los componentes de interfaz de usuario e interfaces. Las Interfaces se muestran en color gris, las clases abstractas en naranja, y las clases regulares en azul. Una versión anotada del diagrama aparece en la Hoja de Referencia de Vaadin.

Figura 5.1 Diagrama de Herencia de los Componentes de Interfaz de Usuario

En la parte superior de la jerarquía de interfaces, tenemos la interfaz Component. En la parte superior de la jerarquía de clases, tenemos la clase AbstractComponent. Que es heredada por otras dos clases abstractas AbstractField, heredada ademas por campos de componentes, y AbstractComponentContainer, heredada de varios contenedores y componentes de diseño. Los componentes que no están vinculados a un contenido de modelo de datos, tales como etiquetas y enlaces, heredan directamente de AbstractComponent.

El diseño de los distintos componentes en una ventana es controlada, lógicamente, por componentes de diseño, al igual que el convencional conjunto de herramientas de interfaz de usuario de Java para aplicaciones de escritorio. Además, con el componente CustomLayout, puede escribir un diseño personalizado como una plantilla XHTML que incluye la ubicación de cualquiera de los componentes contenido. Al mirar el diagrama de herencia, podemos ver que los componentes de diseño heredan las interfaces AbstractComponentContainer y Layout. Los componentes de diseño se describen en detalle en el Capítulo 6, Administrar el Diseño.

Mirándolo desde la perspectiva de una jerarquía de objetos, tendríamos un objeto Window, que contiene una jerarquía de componentes de diseño, que a su vez contienen otros componentes de diseño, componentes de campo, y otros componentes visibles.

Puede navegar por los componentes de interfaz de usuario incorporados en la librería Vaadin en la aplicación Sampler de la Demo de Vaadin. Sampler muestra una descripción, documentación JavaDoc, y un código de ejemplo para cada uno de los componentes.

Además de los componentes incorporados, muchos componentes están disponibles como complementos, ya sea desde el Directorio de Vaadin o desde fuentes independientes. Existen tanto componentes comerciales como libres. La instalación de los complementos se describe en el Capítulo 15, Usar Complementos Vaadin.

Apunte Escondido Vaadin y Refcard

La Figura 5.1, "Diagrama de Herencia de los Componentes de Interfaz de Usuario" es incluida en el Apunte Escondido Vaadin que muestra la jerarquía de la relación básica de los componentes de interfaz de usuario y las clases para vincular datos e interfaces. Puede descargarlo en http://dev.vaadin.com/browser/doc/trunk/cheatsheet/vaadin-cheatsheet-duplex.pdf.

El diagrama también es incluido en las seis páginas del DZone Refcard, el cual puede encontrarlo en https://vaadin.com/refcard.



Anterior
4.8. Configurar el Entorno de la Aplicación
Siguiente
5.2. Interfaces y Abstracciones

4.8. Configurar el Entorno de la Aplicación

Las aplicaciones Vaadin se despliegan como aplicaciones web Java. Una "aplicación web" Java puede contener un número de servlets, de los cuales cada uno puede ser una aplicación Vaadin o algún otro servlet, y recursos estáticos como archivos HTML. Dicha aplicación web es normalmente empaquetada como un archivo WAR (Web application ARchive), el cual se puede desplegar en un servidor de aplicaciones Java (o un contenedor de servlets para ser exactos).

Para ver un tutorial detallado sobre cómo se empaquetan las aplicaciones web, por favor, refiérase a cualquier libro de Java que trate de Java Servlets. Sun tiene una excelente referencia en linea en http://java.sun.com/j2ee/tutorial/1_3-fcs/doc/WCC3.html.

Recuerde que, en el lenguaje Servlet Java, una "aplicación web" significa un conjunto de servlets Java o portlets, páginas HTML y JSP estáticas, y otros recursos que componen una aplicación. Una aplicación web Java es típicamente envasada como un paquete WAR para ser desplegado. Por otro lado, las aplicaciones Vaadin, se ejecutan como servlets dentro de esa aplicación web Java. Existe también otros tipos de aplicaciones web. Para evitar confusión con el significado general de "aplicación web", en este libro a menudo nos referiremos a la aplicación web Java como "WAR".

4.8.1 Crear un WAR Desplegable en Eclipse

Para desplegar una aplicación en un servidor web, necesita crear un paquete WAR. Aquí proporcionamos las instrucciones para Eclipse.
  1. Seleccione File → Export y luego Web → WAR File. O, en el Project Explorer haga clic derecho en el proyecto y seleccione Web → WAR File.
  2. En Web project seleccione el proyecto a exportar. En Destination Ingrese el nombre de archivo (.war).
  3. Realice cualquier otros ajustes en el cuadro de diálogo y haga clic en Finish.
4.8.2 Contenido de una Aplicacione Web

Los siguientes archivos son necesarios en una aplicación web a fin de ejecutarlo.

Organización de una aplicación web
  • WEB-INF/web.xml
    Este es el descriptor de aplicaciones web estándar que define cómo se organiza la aplicación. Puede referirse a cualquier libro de Java sobre el contenido de este archivo. También vea un ejemplo en el Ejemplo 4.1, "web.xml".

    WEB-INF/lib/vaadin-6.x.x.jar
    Esta es la librería Vaadin. Está incluida en el paquete del producto en el directorio lib.

    Sus clases de la aplicación
    Debe incluir sus clases de la aplicación, ya sea en un archivo JAR en WEB-INF/lib o como clases en WEB-INF/classes

    Sus archivos propios de tema (OPCIONAL)
    Si la aplicación utiliza un tema en especial (apariencia), debe incluirlo en el directorio VAADIN/themes/nombretema.

    Conjunto de Widget (OPCIONAL)
    Si su aplicación utiliza un conjunto de proyectos widget específicos, deben ser compilados en el directorio VAADIN/widgetset/.
4.8.3 El Descriptor de Despliegue web.xml

El descriptor de despliegue es un archivo XML con el nombre de web.xml en el directorio WEB-INF de una aplicación web. Se trata de un componente estándar en Java EE que describe cómo una aplicación web debe ser desplegada. La estructura del descriptor de despliegue se ilustra en el siguiente ejemplo. Sólo tiene que desplegar aplicaciones como servlets implementado por el contenedor de la clase especial com.vaadin.terminal.gwt.server.ApplicationServlet. La clase de la aplicación actual se especifica proporcionando el parámetro application con el nombre de la clase específica de la aplicación para el servlet. El servlet es entonces conectado a una URL de una manera estándar para Servlets Java.

Ejemplo 4.1. web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app
  id="WebApp_ID" version="2.4"
  xmlns="http://java.sun.com/xml/ns/j2ee" 
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
  xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee
     http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">

  <servlet>
    <servlet-name>miservlet</servlet-name>
    <servlet-class>
        com.vaadin.terminal.gwt.server.ApplicationServlet
    </servlet-class>
    <init-param>
      <param-name>application</param-name>
      <param-value>MiClaseAplicacion</param-value>
    </init-param>
  </servlet>

  <servlet-mapping>
    <servlet-name>miservlet</servlet-name>
    <url-pattern>/*</url-pattern>
 </servlet-mapping>
</web-app>

El descriptor define un servlet con el nombre de miservlet. La clase del servlet, com.vaadin.terminal.gwt.server.ApplicationServlet, es proporcionada por el framework Vaadin y debe ser el mismo para todos los proyectos Vaadin. El servlet toma el nombre de la clase Calc de la clase de la aplicación del usuario como parámetro, incluyendo la ruta completa del paquete a la clase. Si la clase está en el paquete por defecto, la ruta del paquete obviamente no es utilizada.

url-pattern es definida arriba como /*. Esto coincide con cualquier URL en el contexto del proyecto. Hemos definido anteriormente el contexto del proyecto como miproyecto por lo que la URL de la aplicación será http://localhost:8080/miproyecto/. Si el proyecto fuese a tener varias aplicaciones o servlets, tendrían que tener diferentes nombres para distinguirlos. Por ejemplo, url-pattern /miapp/* coincidiría con una URL como http://localhost:8080/miproyecto/miapp/. Observe que la barra y el asterisco deben ser incluidos al final del patrón.

Observe también que si el patrón de la URL es distinta de la raíz /* (por ejemplo /miapp/*), también tendrá que hacer un mapeo de servlet a /VAADIN/* (a menos que lo sirva estáticamente como se indica a continuación). Por ejemplo:

    ...
    <servlet-mapping>
        <servlet-name>myservlet</servlet-name>
        <url-pattern>/myurl/*</url-pattern>
    </servlet-mapping>

    <servlet-mapping>
        <servlet-name>myservlet</servlet-name>
        <url-pattern>/VAADIN/*</url-pattern>
    </servlet-mapping>

Si tiene varios servlets, debe especificar sólo un mapeo a /VAADIN/*. No importa a que servlet mapee el patrón, siempre y cuando se trate de un servlet Vaadin.

No tiene que proporcionar el anterior mapeo /VAADIN/* si sirve tanto en el conjunto de widgets como en los temas estáticos (personalizado y por defecto) en el directorio WebContent/VAADIN/. El mapeo permite simplemente servirlos dinámicamente desde el JAR de Vaadin. Se recomienda servirlos estáticamente para entornos de producción, ya que es mucho más rápido. Si va a servir el contenido dentro de la misma aplicación web, no puede tener el patrón raíz /* para el servlet Vaadin, ya que entonces todas las solicitudes se mapearían al servlet.

Parámetros del Descriptor de Despliegue
El descriptor de despliegue puede tener muchos parámetros y opciones que controlan la ejecución de un servlet. Puede encontrar una documentación completa del descriptor de despliegue en la Especificación de Servlet Java en http://java.sun.com/products/servlet/.

Por defecto, las aplicaciones Vaadin se ejecutan en modo de depuracion, el cual debería ser utilizado durante el desarrollo. Esto permite varias características de depuración. Para utilizar producción, debe poner en el web.xml el siguiente parámetro:

<context-param>
 <param-name>productionMode</param-name>
 <param-value>true</param-value>
 <description>Vaadin production mode</description>
</context-param>

El parámetro y los modos de depuración y producción se describen en detalle en la Sección 12.4, "Modo de Depuracion y Producción".

Una opción que a menudo es necesaria es el tiempo de espera de sesión. Diferentes contenedores de servlet utilizan valores predeterminados para diferentes tiempos de espera, como 30 minutos para Apache Tomcat. Puede configurar el tiempo de espera con:

<session-config>
    <session-timeout>30</session-timeout>
</session-config>

Después de que el tiempo de espera haya caducado, será llamado el método close() de la clase Application. Debe implementarlo si desea controlar situaciones de tiempo de espera.



Anterior
4.7. Controlar Errores
Siguiente
Capítulo 5. Componentes de Interfase de Usuario

4.7. Controlar Errores

4.7.1 Indicador y mensaje de Error

Todos los componentes tienen un indicador de error incorporado que se puede establecer explícitamente con setComponentError() o se puede activar implícitamente si la validación del componente falla. Como con el componente caption, la ubicación del indicador es manejado por el diseño en el que se encuentre el componente. Por lo general, el indicador de error es ubicado a la derecha del texto del título. Al pasar el puntero del ratón sobre el campo muestra el mensaje de error.

El siguiente ejemplo muestra cómo se puede establecer el error del componente de forma explícita. El ejemplo básicamente valida el valor del campo sin necesidad de utilizar un validador actual.

// Crear un campo.
final TextField campoTexto = new TextField("Introduzca el código");
principal.addComponent(campoTexto);

// Deje que el componente de error sea limpio inicialmente .
campoTexto.setComponentError(null); // (realmente el valor predeterminado)

// Tener un botón a la derecha del campo (y alinearlo apropiadamente).
final Button boton = new Button("Aceptar!");
principal.addComponent(boton);
((VerticalLayout)principal.getContent())
        .setComponentAlignment(boton, Alignment.BOTTOM_LEFT);

// Controlar los clics del botón
boton.addListener(new Button.ClickListener() {
    public void botonClick(ClickEvent event) {
        // Si el valor del campo es incorrecto, establecer su error.
        // (Permitir sólo caracteres alfanuméricos.)
        if (! ((String) campoTexto.getValue()).matches("^\\w*$")) {
            // Colocar el componente en estado de error y
            // establecer el mensaje de error.
            campoTexto.setComponentError(
                new UserError("Debe ser letras y números"));
        } else {
            // De lo contrario, limpiarlo.
            campoTexto.setComponentError(null);
        }
    }
});

Figura 4.7. Indicador de error activo

El componente Form controla y muestra también los errores de los campos contenidos de modo que se muestra tanto el indicador de error como el mensaje en un área especial del indicador de error. Consulte la Sección 5.19, "Form" y la Sección 5.19.3, "Validar la Entrada del Formulario" para obtener detalles sobre el componente Form y la validación de la entrada del formulario.

4.7.2 Notificaciones

Las notificaciones son errores o cajas de información que aparecen normalmente en el centro de la pantalla. Un cuadro de notificación tiene un título y una descripción opcional e icono. La caja se queda en la pantalla durante un tiempo determinado o hasta que el usuario haga clic en él. El tipo de notificación define la apariencia y el comportamiento predeterminado de una notificación.

Las notificaciones están siempre asociadas con un objeto window, que puede ser una ventana secundaria (la posición siempre es con relación a la vista completa del navegador). La clase Window proporciona un método showNotification() para mostrar las notificaciones. El método lleva como parámetros un título y una descripción opcional y el tipo de notificación. El método también acepta un objeto de la notificación del tipo Window.Notification, como se describe más adelante.

ventanaPrincipal.showNotification("Este es el título",
                            "Esta es la descripción");

Figura 4.8. Notificacion

El título y la descripción son, por defecto, escritos en la misma línea. Si desea tener un salto de línea entre ellos, utilice el marcado XHTML de salto de línea de "<br/>". Puede utilizar cualquier código XHTML en el título y la descripción de una notificación. Si es posible obtener el contenido de la notificación desde la entrada del usuario, debería sanear cuidadosamente el contenido, como se señala en la Sección 12.9.1, "Sanear la Entrada del Usuario para Prevenir Agujeros de Seguridad".

principal.showNotification("Esta es una advertencia",
            "<br/>Esta es la <i>última</i> advertencia",
            Window.Notification.TYPE_WARNING_MESSAGE);

Figura 4.9. Notificacion con Formato

El tipo de notificación define el estilo predeterminado y comportamiento global de una notificación. Si no hay ningún tipo de notificación el tipo utilizado es, "humanized" como valor predeterminado. Los tipos de notificación, se enumeran a continuación, son definidos en la clase Window.Notification.

TYPE_HUMANIZED_MESSAGE

Un mensaje fácil de utilizar que no molesta demasiado: no requiere confirmación haciendo clic y desaparece rápidamente. Se centra y tiene un color gris neutro.

TYPE_WARNING_MESSAGE

Las advertencias son mensajes de mediana importancia. Son mostradas con colores que no son ni neutrales ni tampoco distraen. Una advertencia es mostrada durante 1.5 segundos, pero el usuario puede hacer clic en el cuadro del mensaje para desestimarla. El usuario puede seguir interactuando con la aplicación, mientras que se muestra la advertencia.

TYPE_ERROR_MESSAGE

Los mensajes de error son notificaciones que requieren una mayor atención por parte del usuario, con colores de alerta y requieren que el usuario haga clic en el mensaje para descartarlo. El cuadro de mensaje de error no incluye una instrucción para hacer clic en el mensaje, aunque el cuadro de cierre en la esquina superior derecha lo indica visualmente. A diferencia de las otras notificaciones, el usuario no puede interactuar con la aplicación, mientras que es mostrado el mensaje de error.

TYPE_TRAY_NOTIFICATION

Las notificaciones de bandeja son mostradas en el área de la "bandeja del sistema", es decir, en la esquina inferior derecha de la vista del navegador. Como no suelen ocultar ninguna interfaz de usuario, se muestra por más tiempo que los mensajes tipo humanized o warning, por defecto 3 segundos. El usuario puede seguir interactuando con la aplicación normalmente mientras se muestra la bandeja de notificación.

Todas las características específicas de los tipos de notificaciones pueden ser controladas con los atributos de Window.Notification. Puede pasar explícitamente un objeto de notificación creado al método showNotification().

// Crear una notificación con la configuración por defecto para una advertencia.
Window.Notification notif = new Window.Notification(
        "Tenga cuidado!!",
        "Este mensaje se esconde en la esquina superior izquierda!",
        Window.Notification.TYPE_WARNING_MESSAGE);
 
// Establecer la posición.
notif.setPosition(Window.Notification.POSITION_TOP_LEFT);
 
// Que se quede allí hasta que el usuario haga clic en él
notif.setDelayMsec(-1);
 
// Mostrar en la ventana principal.
principal.showNotification(notif);

El método setPosition() permite establecer la posición de la notificación. El método toma como parámetro cualquiera de las constantes:

Window.Notification.POSITION_CENTERED
Window.Notification.POSITION_CENTERED_TOP
Window.Notification.POSITION_CENTERED_BOTTOM
Window.Notification.POSITION_TOP_LEFT
Window.Notification.POSITION_TOP_RIGHT
Window.Notification.POSITION_BOTTOM_LEFT
Window.Notification.POSITION_BOTTOM_RIGHT


El método setDelayMSec() le permite establecer el tiempo en milisegundos durante cuánto tiempo se muestra la notificación. El valor del parámetro -1 significa que el mensaje se muestra hasta que el usuario haga clic en el cuadro de mensaje. Esto también impide la interacción con otras partes de la ventana de la aplicación, como es el comportamiento predeterminado de los mensajes de error. Sin embargo, esto no, agrega el cuadro de cierre que tiene la notificación de error.

4.7.3 Personalizar los Mensajes del Sistema

Los mensajes del sistema son notificaciones que indican un estado importante no válido en una aplicación que por lo general requiere reiniciar la aplicación. El tiempo de espera de una sesión es tal vez el ejemplo más típico de estado.

Los mensajes del sistema son cadenas administradas en la clase SystemMessages.
  • sessionExpired
    La sesión del servlet Application ha expirado. Una sesión expira si no se hacen solicitudes al servidor durante el intervalo de tiempo de espera de la sesión. El intervalo de tiempo de espera de la sesión puede ser configurado con el parámetro session-timeout en web.xml, como se describe en la Sección 4.8.3, "El Descriptor de Despliegue web.xml".

    communicationErrorURL
    Un problema de comunicación no especificado entre el Motor del Lado del Cliente Vaadin y el servidor de aplicaciones. El servidor puede estar no disponible o hay algún otro problema.

    authenticationError
    Este error se produce si es recibida una respuesta 401 (No autorizado) a una petición desde el servidor.

    internalError
    Un problema interno serio, posiblemente indicando un error de programación en el Motor de Lado del Cliente Vaadin o en algún código personalizado del lado del cliente.

    outOfSync
    El estado del lado del cliente no es válido con respecto al estado del lado del servidor.

    cookiesDisabled
    Informa al usuario que las cookies están desactivadas en el navegador y la aplicación no funciona sin ellas.

Cada mensaje tiene cuatro propiedades: un título corto, el mensaje actual, una dirección URL para redireccionar después de mostrar el mensaje, y la propiedad que indica si la notificación está activada.

Los detalles adicionales pueden ser escritos (en Inglés) en la ventana de la consola de depuración descrito en la Sección 12.4, "Modo de Depuracion y Producción".

Puede sobrescribir los mensajes predeterminados del sistema implementando el método getSystemMessages() en la clase application. El método debe devolver un Application.SystemMessages. La manera más fácil de personalizar los mensajes es utilizar un objeto CustomizedSystemMessages de la siguiente manera:

// Sobreescribe la implementación predeterminada
public static SystemMessages getSystemMessages() {
    CustomizedSystemMessages mensajes =
            new CustomizedSystemMessages();
    mensajes.setSessionExpiredCaption("Ohno, la sesión ha finalizado!");
    mensajes.setSessionExpiredMessage("No funciona en reposo!");
    mensajes.setSessionExpiredNotificationEnabled(true);
    mensajes.setSessionExpiredURL("http://vaadin.com/");
    return mensajes;
}

Observe que el método especial getSystemMessages() no está definido en una interfaz, tampoco existe en la superclase Application.

4.7.4 Controlar Excepciones No Detectadas

El desarrollo de aplicaciones con Vaadin sigue el modelo de programación orientada a eventos. Los eventos del ratón y teclado causan en el cliente (por lo general de más alto nivel) eventos del lado del servidor, que pueden ser controlados con oyentes, y así es como funciona la mayor parte de la lógica de la aplicación. Controlar los eventos puede dar lugar a excepciones ya sea en la lógica de la aplicación o en el mismo framework, pero algunos de ellos no pueden ser capturados correctamente.

Por ejemplo, en el siguiente fragmento de código, lanzamos un error en un oyente de eventos, pero este no lo captura, por lo que el framework se cae.

final Button boton = new Button ("Fállame");

boton.addListener(new Button.ClickListener() {
    public void botonClick(ClickEvent event) {
        // Lanzar alguna excepción.
        throw new RuntimeException("Usted no puede atrapar esto.");
    }
});

Cualquier excepción que se produzcan en la cadena de llamada, pero que no es capturada en ningún otro nivel, son finalmente capturadas por el adaptador de terminal en ApplicationServlet, el componente de más bajo nivel que recibe las peticiones del cliente. El adaptador de terminal pasa todas las excepciones como eventos al oyente de error de la instancia Application a través de la interfaz Terminal.ErrorListener. La clase Application por defecto, no lanza, reenvió de excepciones.

La razón de esta lógica de control de errores se encuentra en la lógica que controla la sincronización del estado del componente entre el cliente y el servidor. Queremos controlar todos los cambios variables serializados en la petición del cliente, porque de lo contrario el estado de los componentes del lado del cliente y del lado del servidor se des sincronizarian con mucha facilidad, lo que podría poner toda la aplicación en un estado inválido.

La implementación predeterminada de la interfaz Terminal.ErrorListener en la clase Application simplemente imprime el error en la consola. Este también intenta encontrar un componente relacionado con el error. Si la excepción se produjo en un oyente vinculado a un componente, el componente es considerado como el componente relacionado a la excepción. Si es encontrado un componente relacionado, el controlador de errores establece el error del componente para él, puede establecer el mismo atributo con setComponentError().

En la interfaz de usuario, el error del componente es mostrado con un pequeño signo "!" de color rojo (en el tema por defecto). Si mueve el puntero del ratón sobre él, podrá ver el seguimiento inverso entero de la excepción en un gran cuadro de descripción, como se ilustra en la Figura 4.10, "Excepción No Detectada en el Indicador de Error del Componente" para el anterior código de ejemplo.

Figura 4.10. Excepción No Detectada en el Indicador de Error del Componente

Puede cambiar fácilmente la lógica para controlar los errores terminales sobrescribiendo el método terminalError() en su clase de aplicación (la que hereda Application) o estableciendo un oyente de error personalizado con el método setErrorHandler. Puede descartar con seguridad el control por defecto o ampliar su uso con su control de error personalizado o sistema de registro. En el siguiente código de ejemplo, las excepciones también son reportadas como notificaciones en la ventana principal.

@Override
public void terminalError(Terminal.ErrorEvent event) {
    // Llamar a la implementación por defecto.
    super.terminalError(event);

    // Algún comportamiento personalizado.
    if (getMainWindow() != null) {
        getMainWindow().showNotification(
                "Se produjo una excepción sin control!",
                event.getThrowable().toString(),
                Notification.TYPE_ERROR_MESSAGE);
    }
}

El control de otras excepciones funcionan del modo habitual para los Servlets Java. Las excepciones no detectadas son finalmente detectadas y controladas por el servidor de aplicaciones.



Anterior
4.6. Apagar una Aplicación
Siguiente
4.8. Configurar el Entorno de la Aplicación

4.6. Apagar una Aplicación

Un usuario puede cerrar la sesión o cerrar la página web o el navegador, por lo que una sesión y la instancia application asociada pueden terminarse. La finalización de una aplicación puede ser iniciada por la lógica de la aplicación. De lo contrario, será terminada automáticamente cuando la sesión del Servlet expire.

4.6.1 Cerrar una Aplicación

Si el usuario sale de la aplicación a través de la interfaz de usuario, un controlador de eventos debería llamar al método close() en la clase Application para cerrar la sesión.

En el siguiente ejemplo, tenemos un botón Salir, que termina la sesión del usuario.

Button botonCerrar = new Button("Salir");

botonCerrar.addListener(new Button.ClickListener() {
    @Override
    public void buttonClick(ClickEvent event) {
        getMainWindow().getApplication().close();
    } 
});
 
principal.addComponent(botonCerrar);

Notará que el cierre de la aplicación, simplemente recarga la aplicación con una nueva instancia de Application. Puede configurar la ventana para redirigir a una dirección URL diferente (que no recarga la aplicación) con setLogoutURL. En su clase application, escriba:

setLogoutURL("/salir.html");

4.6.2 Controlar el Cierre de una Ventana

El cierre de la ventana principal (o todas las ventanas de nivel de aplicación) no cierra la sesión y la instancia de la aplicación quedará colgando. Necesitará programar tal comportamiento controlando los eventos de cierre de las ventanas.

Si el usuario cierra una ventana del navegador, como la ventana principal o cualquier otra ventana de nivel de aplicación, la ventana enviará una petición final AJAX al servidor, el cual disparará un Window.CloseEvent para la ventana cerrada. Puede controlar el evento con un Window.CloseListener. En caso de que el usuario cierre el navegador, el evento es disparado para cada ventana abierta.

// Cerrar la aplicación si la ventana principal es cerrada.
principal.addListener(new Window.CloseListener(){
   @Override
    public void windowClose(CloseEvent e) {
       System.out.println("Cerrando la aplicación");
       getMainWindow().getApplication().close();
    } 
});

Tenga en cuenta que actualizar una ventana significa cerrarla y volverla a abrir. Por lo tanto, si tiene un controlador de cierre como el anterior, el usuario pierde la posibilidad de actualizar la ventana del navegador.

En el probable caso de que el navegador se bloquee, ningún evento de cierre es comunicado al servidor. Como el servidor no tiene manera de conocer acerca del problema, y la sesión quedará colgando hasta que expire el tiempo de espera de la sesión. Durante este tiempo, el usuario puede reiniciar el navegador, abrir el URL de la aplicación, y la ventana principal le mostrará donde el usuario le dejó. Esto puede ser un comportamiento deseado en muchos casos, pero a veces no lo es y puede crear un problema de seguridad.



Anterior
4.5. Referenciar Recursos
Siguiente
4.7. Controlar Errores

4.5. Referenciar Recursos

Las aplicaciones web trabajan sobre la web y tienen varios recursos, como imágenes o archivos descargables, que el navegador web tiene que obtener desde el servidor. Estos recursos son utilizados típicamente en los componentes de interfaz de usuario Embedded (imágenes) o Link (archivos descargables). Varios componentes, tales como TabSheet, también pueden incluir iconos, los cuales también son manejados como recursos.

Un servidor web puede manejar muchas de estas peticiones de recursos estáticos sin tener que pedirlas desde la aplicación, o desde el que el objeto Application puede proporcionarnos. Para los recursos dinámicos, la aplicación de usuario debe ser capaz de crearlos dinámicamente. Vaadin proporciona interfaces de petición de recursos para las aplicaciones de modo que puedan devolver los diversos tipos de recursos, tales como archivos o recursos creados dinámicamente. Esto incluye las clases StreamResource y URI y controladores de parámetros descritos en la Sección 12.5.1, "Controlador de URI" y la Sección 12.5.2, "Controlador de Parametros", respectivamente.

Vaadin proporciona también herramientas de bajo nivel para la recuperación del URI y otros parámetros de una petición HTTP. En primer lugar, examinaremos cómo las aplicaciones pueden ofrecer diversos tipos de recursos y luego buscar en interfaces de bajo nivel para el manejo de los URIs y parámetros para proporcionar los recursos y funcionalidades.

Observe que el uso del URI o controladores de parámetro para crear "páginas" no es significativo en Vaadin o en aplicaciones AJAX en general. Por favor, consulte la Sección 12.1, "Caracteristicas Especiales de Aplicaciones AJAX" para una explicación detallada.

4.5.1. Interfaz y Clases Resource

Vaadin tiene dos interfaces para los recursos: una interfaz genérica Resource y una interfaz ApplicationResource más específica para los recursos proporcionados por la aplicación.

Figura 4.4. Diagrama de la Interfaz y Clase Resource

Los recursos de ApplicationResource son manejados por la clase Application. Cuando usted crea tal recurso, le da el objeto application al constructor. El constructor registra el recurso en la aplicación usando el método addResource.

Application maneja peticiones de los recursos y permite acceder a los recursos utilizando un URI. El URI consiste en el nombre de la base de la aplicación y un nombre relativo del recurso. El nombre relativo es "APP/"+iddelrecurso+"/"+nombredelarchivo, por ejemplo "APP/1/miimagen.png". iddelrecurso es un identificador numérico generado para hacer recursos únicos, y nombredelarchivo es el nombre de fichero del recurso proporcionado en el constructor de su clase. Sin embargo, la aplicación que utiliza un recurso no suele tener en cuenta su URI. Sólo tiene que dar el recurso a un Embedded o Link apropiado o a algún otro componente de interfaz de usuario, que maneje el renderizado del URI.

4.5.2. Recursos de Archivos

Los recursos de archivos son archivos almacenados en cualquier parte del sistema de archivos. El uso de los recursos de archivo generalmente, se divide en dos categorías principales: los archivos descargados y las imágenes embebidas.

Un objeto file que puede ser accedido como un recurso de archivo es definido con la clase estándar java.io.File. Puede crear el archivo, ya sea con una ruta absoluta o con una relativa, pero la ruta base de la ruta de acceso relativa depende de la instalación del servidor web. Por ejemplo, en Apache Tomcat, el directorio actual por defecto es la ruta de instalación de Tomcat.

4.5.3. Recursos de Cargador de Clase

ClassResource permite que los recursos sean cargados desde el paquete desplegado de la aplicación utilizando el Cargador de Clase de Java. En el ejemplo de una línea más abajo, se carga un recurso de imagen desde el paquete de la aplicación y es mostrado en un componente Embedded.

ventanaPrincipal.addComponent(new Embedded ("",
        new ClassResource("sonriente.jpg",
                  ventanaPrincipal.getApplication())));

4.5.4. Recursos de Tema (Estilo)

Los recursos de tema de la clase ThemeResource son archivos, generalmente imagenes, incluidas en un tema. Un tema es localizado en la ruta camino VAADIN/themes/nombredeltema en una aplicación web. El nombre de un recurso de tema es proporcionado como parámetro para el constructor, con una ruta relativa a la carpeta themes.

// Un recurso de tema en el actual tema ("book-examples")
// Localizado en: VAADIN/themes/book-examples/img/themeimage.png
ThemeResource recurso = new ThemeResource("img/themeimage.png");
 
// utilizar el recurso
Embedded imagen = new Embedded("Mi Imagen del Tema", recurso);

El resultado se muestra en la Figura 4.5, "Recursos de Tema", ilustra también la estructura de la carpeta para el archivo de recursos del tema en un proyecto de Eclipse.

Figura 4.5. Recursos de Tema

Para utilizar recursos de tema, debe establecer el tema para la aplicacion. Consulte el Capítulo 8, Temas para mayor informacion sobre los temas.

4.5.5. Recursos de Flujo

Los recursos de flujo son recursos de aplicaciones que permiten la creación de contenido dinámico de recursos. Los gráficos son ejemplos típicos de imágenes dinámicas. Para definir un recurso de flujo, es necesario implementar la interfaz StreamResource.StreamSource y su método getStream. El método debe devolver un InputStream desde el cual el flujo pueda ser leído.

El siguiente ejemplo muestra la creación de una simple imagen en formato de imagen PNG.

import java.awt.image.*;

public class MiFuenteImagen
             implements StreamResource.StreamSource {
    ByteArrayOutputStream bufferImagen = null;
    int recargas = 0;
    
    /* Necesitamos implementar este método que devuelve
     * el recurso como un flujo. */
    public InputStream getStream () {
        /* Crear una imagenn y dibujar algo en ella. */
        BufferedImage imagen = new BufferedImage (200, 200,
                               BufferedImage.TYPE_INT_RGB);
        Graphics dibujable = imagen.getGraphics();
        dibujable.setColor(Color.lightGray);
        dibujable.fillRect(0,0,200,200);
        dibujable.setColor(Color.yellow);
        dibujable.fillOval(25,25,150,150);
        dibujable.setColor(Color.blue);
        dibujable.drawRect(0,0,199,199);
        dibujable.setColor(Color.black);
        dibujable.drawString("Recargas = " + recargas, 75, 100);
        recargas++;

        try {
            /* Escriba la imagen en un búfer. */
            bufferImagen = new ByteArrayOutputStream();
            ImageIO.write(imagen, "png", bufferImagen);
            
            /* Devolver un flujo desde el buffer. */
            return new ByteArrayInputStream(
                         bufferImagen.toByteArray());
        } catch (IOException e) {
            return null;
        }
    }
}

El contenido de la imagen generada es dinámico, ya que actualiza el contador recargas en cada llamada. El método ImageIO escribe la imagen para un flujo de salida, mientras, tuvimos que devolver un flujo de entrada, por lo que almacenamos los contenidos de la imagen en un búfer temporal.

Puede utilizar los recursos de varias maneras. Algunos componentes de interfaz de usuario, como Link y Embedded, toman sus parámetros como un recurso.

A continuación se mostrará la imagen con el componente Embedded. El constructor StreamResource obtiene una referencia de la aplicación y se registra en los recursos de la aplicación. Supongamos que principal es una referencia a la ventana principal y this es el objeto application.

// Crear una instancia de nuestra fuente de flujo.
StreamResource.StreamSource fuenteImagen = new MiFuenteImagen();
 
// Crear un recurso que utiliza el origen de flujo y proporcinarle un nombre.
// El constructor registrará automáticamente el recurso en
// la aplicación.
StreamResource recursoImagen =
        new StreamResource(fuenteImagen, "miImagen.png", this);
 
// Crear un componente integrado que recibe su contenido
// del recurso.
principal.addComponent(new Embedded("Titulo de la imagen", recursoImagen));

La imagen se verá de la siguiente manera:

Figura 4.6. Pantallazo del ejemplo de recurso de flujo con una imagen embebida

Hemos llamado a este recurso como miImagen.png. La aplicación agrega una llave de recurso al nombre de archivo del recurso para hacerlo único. El URI completo será como http://localhost:8080/bancodepruebas/APP/1/miImagen.png. El APP/1/miImagen.png al final es la parte relativa del URI. Puede obtener la parte relativa de los recursos del URI desde la aplicación con Application.getRelativeLocation().

Otra solución para crear contenido dinámico es un controlador del URI, posiblemente junto con un controlador de parámetro. Vea la Sección 12.5.1, "Controlador de URI" y la Sección 12.5.2, "Controlador de Parametros".



Anterior
4.4. Controlar Eventos con Oyentes
Siguiente
4.6. Apagar una Aplicación

4.4. Controlar Eventos con Oyentes

Vamos a poner en práctica lo que aprendimos del manejo de eventos en la Sección 3.5, "Eventos y Oyentes". Puede manejar los eventos de tres formas básicas, como se muestra a continuación.

El siguiente ejemplo sigue un modelo típico en el que tiene un componente Button y un oyente que se encarga de la interacción del usuario (clics) comunicado a la aplicación por eventos. Aquí definimos una clase que escucha los eventos clic.

public class ElBoton implements Button.ClickListener {
    Button elBoton;

    /** Crear el botón en el contenedor proporcionado. */
    public ElBoton(AbstractComponentContainer contenedor) {
        elBoton = new Button ("No presione este botón");
        elBoton.addListener(this);
        contenedor.addComponent(elBoton);
    }
    
    /** Controlar los eventos clic del boton para el boton. */
    public void buttonClick (Button.ClickEvent event) {
        elBoton.setCaption ("No presione este botón de nuevo");
    }
}

Como una aplicación suele recibir eventos de varios componentes de la misma clase, tales como múltiples botones, esta tiene que ser capaz de distinguir entre los componentes individuales. Hay varias técnicas para hacer esto, pero probablemente la más fácil es utilizar la propiedad del evento recibido, que se establece en el objeto enviando el evento. Esto requiere tener a mano una referencia para cada objeto que emite los eventos.

public class ElBoton implements Button.ClickListener {
    Button elBoton;
    Button segundoBoton;

    /** Crear dos botones en el contenedor proporcionado. */
    public ElBoton(AbstractComponentContainer contenedor) {
        elBoton = new Button ("No presione este botón");
        elBoton.addListener(this);
        contenedor.addComponent(elBoton);
        
        segundoBoton = new Button ("Yo soy un botón también");
        segundoBoton.addListener(this);
        contenedor.addComponent (segundoBoton);
    }
    
    /** Controlar los eventos clic del boton para los dos boton. */
    public void buttonClick (Button.ClickEvent event) {
        if (event.getButton() == elBoton)
            elBoton.setCaption("No presione este botón de nuevo");
        else if (event.getButton() == segundoBoton)
            segundoBoton.setCaption("Yo no soy un número");
    }
}

Otra solución para manejar eventos múltiples de la misma clase implica vincular un origen de eventos a un método oyente en lugar de la clase. Un evento puede ser vinculado a un método utilizando otra versión del método addListener(), el cual toma el método que maneja el evento como un parámetro. El método puede ser pasado ya sea por el nombre del método o como un objeto Method. En el siguiente ejemplo, utilizamos el nombre del método como una cadena (el cual no es comprobado en tiempo de compilación).

public class ElBoton2 implements Button.ClickListener {
    Button elBoton;
    Button segundoBoton;

    /** Crear dos botones en el contenedor proporcionado. */
    public ElBoton2(AbstractComponentContainer contenedor) {
        elBoton = new Button ("No presione este botón");
        elBoton.addListener(Button.ClickEvent.class, this,
                              "elButtonClick");
        contenedor.addComponent(elBoton);
        
        segundoBoton = new Button ("Yo soy un botón también");
        segundoBoton.addListener(Button.ClickEvent.class, this,
                                 "segundoButtonClick");
        contenedor.addComponent (segundoBoton);
    }
    
    public void elButtonClick (Button.ClickEvent event) {
        elBoton.setCaption ("No presione este botón de nuevo");
    }

    public void segundoButtonClick (Button.ClickEvent event) {
        segundoBoton.setCaption("Yo no soy un número");
    }
}

Agregar un método oyente con addListener() es realmente sólo un wrapper que crea un objeto oyente com.vaadin.event.ListenerMethod, que es un adaptador desde una clase oyente a un método. Este implementa la interfaz java.util.EventListener y por lo tanto puede trabajar para cualquier origen de eventos utilizando la interfaz. Tenga en cuenta que no todas las clases oyentes necesariamente heredan la interfaz EventListener.

La tercera forma, que utiliza definiciones de clases locales anónimas, es a menudo la más fácil ya que no necesita obstaculizar la administración de la clase con nuevas interfaces o métodos. En el siguiente ejemplo se define una clase anónima que hereda la interfaz Button.ClickListener e implementa el método buttonClick().

public class ElBoton3 {
    Button elBoton;
    Button segundoBoton;

    /** Crear dos botones en el contenedor proporcionado. */
    public ElBoton3(AbstractComponentContainer contenedor) {
        elBoton = new Button ("No presione este botón");

        /* Define un oyente en una clase anonima. */
        elBoton.addListener(new Button.ClickListener() {
            /* Controlar el clic. */
            public void buttonClick(ClickEvent event) {
                elBoton.setCaption (
                        "No presione este botón de nuevo");
            }
        });
        contenedor.addComponent(elBoton);
        
        segundoBoton = new Button ("Yo soy un botón también");
        segundoBoton.addListener(new Button.ClickListener() {
            public void buttonClick(ClickEvent event) {
                segundoBoton.setCaption ("Yo no soy un número!");            
            }
        });
        contenedor.addComponent (segundoBoton);
    }
}

También existen otras técnicas para la separación entre las diferentes fuentes. Ellas incluyen el uso de las propiedades de los objetos, nombres o títulos para separarlos entre ellos. Utilizar títulos o cualquier otro texto visible es generalmente desalentador, ya que puede crear problemas para la internacionalización. Utilizar otras cadenas simbólicas también puede ser peligroso, porque la sintaxis de estas cadenas se verifican sólo en tiempo de ejecución.

Los eventos son generalmente emitidos por el framework, pero también las aplicaciones pueden necesitar emitirlos en algunas situaciones, como cuando se requiere actualizar alguna parte de la interface de usuario. Los eventos pueden emitirse utilizando el método fireEvent(Component.Event) de AbstractComponent. El evento entonces es retransmitido a todos los oyentes del evento en particular de la clase para el objeto. Algunos componentes tienen un tipo de evento por defecto, por ejemplo, un Button tiene una clase Button.ClickEvent anidada y una correspondiente interfaz Button.ClickListener. Estos eventos pueden ser desencadenados con fireComponentEvent().



Anterior
4.3. Ventanas Secundarias
Siguiente
4.5. Referenciar Recursos

4.3. Ventanas Secundarias

Una ventana de nivel de aplicación puede tener varias ventanas secundarias flotantes. Están son manejadas por JavaScript del lado del cliente en tiempo de ejecución con características HTML utilizando Vaadin. Vaadin permite abrir y cerrar ventanas secundarias, refrescar una ventana desde otra, cambiar el tamaño de las ventanas, y desplazar el contenido de la ventana. Las ventanas secundarias normalmente son utilizadas por Ventanas de Diálogo y aplicaciones de Interfaz de Múltiples Documentos. Las ventanas secundarias son por defecto no modales; puede establecerlas a modales como se describe en la Sección 4.3.4, "Ventanas Modales".

Al igual que con todos los componentes de interfaz de usuario, la apariencia de una ventana y sus contenidos son definidos con los temas.

El control del usuario a una ventana secundaria está limitada a mover, redimensionar y cerrar la ventana. Maximizar o minimizar aún no están soportados.

4.3.1. Abrir y Cerrar Ventanas Secundarias

Puede abrir una ventana nueva creando un nuevo objeto Window y agregarlo a la ventana principal con el método addWindow() de la clase Application.

miVentana = new Window("Mi Ventana");
ventanaPrincipal.addWindow(miVentana);


Cierre la ventana de una manera similar, llamando a removeWindow() de la clase Application.

miAplicacion.removeWindow (miVentana);


El usuario puede, por defecto, cerrar una ventana secundaria haciendo clic en el botón de cierre en la esquina superior derecha de la ventana. Puede desactivar el botón estableciendo la ventana como de sólo lectura con setReadOnly(true). Tenga en cuenta que también puede deshabilitar el botón haciéndolo invisible en CSS con el formato "display: none". El problema con esta deshabilitación estética es que un usuario malintencionado podría volver a habilitar el botón y cerrar la ventana, lo que podría causar problemas y, posiblemente, ser un agujero de seguridad. Configurando la ventana como de sólo lectura no sólo desactiva el botón de cierre del lado del cliente, sino que también impide procesar el evento cerrar del lado del servidor.

El siguiente ejemplo demuestra el uso de una ventana secundaria en una aplicación. El ejemplo maneja la ventana con un componente personalizado que contiene un botón para abrir y cerrar la ventana.

/** El componente contiene un botón que permite abrir una ventana. */
public class AbridorVentana extends CustomComponent
                          implements Window.CloseListener {
    Window ventanaPrincipal;  // Referencia a la ventana principal
    Window miVentana;    // La ventana que se abrirá
    Button botonAbrir;  // El botón para abrir la ventana
    Button botonCerrar; // Un botón en la ventana
    Label  explicacion; // Un texto descriptivo

    public AbridorVentana(String label, Window principal) {
        ventanaPrincipal = principal;

        // El componente contiene un botón que abre la ventana.
        final VerticalLayout disenio = new VerticalLayout();
        
        botonAbrir = new Button("Abrir Ventana", this,
                                "abrirButtonClick");
        explicacion = new Label("Explicación");
        disenio.addComponent(botonAbrir);
        disenio.addComponent(explicacion);
        
        setCompositionRoot(disenio);
    }

    /** Controlar los clics de los dos botones. */
    public void abrirButtonClick(Button.ClickEvent event) {
        /* Crear una nueva ventana. */
        miVentana = new Window("Mi Cuadro de Diálogo");
        miVentana.setPositionX(200);
        miVentana.setPositionY(100);

        /* Agregar la ventana dentro de la ventana principal. */
        ventanaPrincipal.addWindow(miVentana);

        /* Escuchar los eventos 'cerrar' para la ventana. */
        miVentana.addListener(this);

        /* Agregar los componentes en la ventana. */
        miVentana.addComponent(
                new Label("Una etiqueta de texto en la ventana."));
        botonCerrar = new Button("Cerrar", this, "cerrarButtonClick");
        miVentana.addComponent(botonCerrar);

        /* Permitir abrir sólo una ventana a la vez. */
        botonAbrir.setEnabled(false);

        explicacion.setValue("Ventana abierta");
    }

    /** Controlar el clic del botón botonCerrar y cierre de la ventana. */
    public void cerrarButtonClick(Button.ClickEvent event) {
        /* Las ventanas son manejadas por el objeto application. */
        ventanaPrincipal.removeWindow(miVentana);

        /* Volver al estado inicial. */
        botonAbrir.setEnabled(true);

        explicacion.setValue("Cerrado con el botón");
    }

    /** En caso de que la ventana sea cerrada de otro modo. */
    public void windowClose(CloseEvent e) {
        /* Volver al estado inicial. */
        botonAbrir.setEnabled(true);

        explicacion.setValue("Cerrado con controles de la ventana");
    }
}

El ejemplo implementa un componente personalizado que hereda la clase CustomComponent. Este consiste de un Button que se utiliza para abrir una ventana y un Label para describir el estado de la ventana. Cuando la ventana es abierta, el botón es deshabilitado. Cuando la ventana es cerrada, el botón es habilitado de nuevo.

Puede utilizar el componente personalizado de arriba en la clase application con:

 public void init() { 
    Window principal = new Window("La Ventana Principal"); 
    setMainWindow(principal);

    principal.addComponent(new AbridorVentana("Abridor de Ventana", principal));
}

Cuando se agrega a una aplicación, la pantalla se verá cómo se ilustra en el siguiente pantallazo:

Figura 4.2. Abriendo una Ventana Secundaria

4.3.2. Posicionar una Ventana

Cuando es creada, una ventana tendrá un tamaño y una posición predeterminada. Puede especificar el tamaño de una ventana con los métodos setHeight() y setWidth(). Puede establecer la posición de la ventana con los métodos setPositionX() y setPositionY().

/* Crear una ventana nueva. */
miVentana = new Window("Mi Cuadro de Dialogo");
 
/* Establecer el tamaño de la ventana. */
miVentana.setHeight("200px");
miVentana.setWidth("400px");
 
/* Establecer la posición de la ventana. */
miVentana.setPositionX(200);
miVentana.setPositionY(50);

Tenga en cuenta que el tamaño de la ventana principal es desconocido y los métodos getHeight y getWidth devolverán -1.

4.3.3. Desplazamiento del Contenido en Ventanas Secundarias

Si una ventana secundaria tiene un tamaño fijo o porcentual y su contenido es demasiado grande para ajustarse al área del contenido, una barra de desplazamiento aparecerá para la dirección en particular. Por otro lado, si la ventana secundaria tiene un tamaño indefinido en la dirección, este se ajustará al tamaño del contenido y nunca obtener una barra de desplazamiento. Las barras de desplazamiento en las ventanas secundarias regulares son manejadas con características HTML, es decir, la propiedad overflow: auto en CSS.

Como Window extiende a Panel, las ventanas también son Scrollable. Tenga en cuenta que la interfaz define un desplazamiento programático, no un desplazamiento por el usuario. Por favor, consulte la Sección 6.6, "Panel".

4.3.4. Ventanas Modales

Una ventana modal es una ventana secundaria que tiene que ser cerrada por el usuario antes de poder continuar para usar la ventana padre. Las ventanas de cuadro de diálogo son generalmente modales. La ventaja de las ventanas modales es la simplificación de la interacción del usuario, que puede contribuir a la claridad de la interfaz de usuario. Las ventanas modales también son fáciles de utilizar desde una perspectiva de desarrollo, porque, la interacción del usuario es aislada para ellos, los cambios en el estado de la aplicación son más limitados, mientras que la ventana modal esté abierta. La desventaja de las ventanas modales es que pueden restringir demasiado el flujo de trabajo.

Figura 4.3. Pantallazo de la Aplicación Demostracion Ventana Modal

Dependiendo de la configuración del tema, la ventana padre puede ser atenuada, mientras la ventana modal esté abierta.

La aplicación demo de Vaadin incluye un ejemplo del uso de ventanas modales. La Figura 4.3, "Pantallazo de la Aplicación Demostracion Ventana Modal" arriba es de la aplicación demo. El ejemplo incluye el código fuente.

Advertencia de Seguridad
La modalidad de las ventanas secundarias es meramente una característica del lado del cliente y puede ser burlado con el ataque por código del lado del cliente. No debe confiar en la modalidad de las ventanas secundarias en situaciones críticas de seguridad tales como las ventanas de inicio de sesión.



Anterior
4.2. Administrar la Ventana Principal
Siguiente
4.4. Controlar Eventos con Oyentes

4.2 Administrar la Ventana Principal

Como se explicó en la Sección 12.1, "Caracteristicas Especiales de Aplicaciones AJAX", una aplicación web de AJAX por lo general se ejecuta en una sola "página web" en una ventana del navegador. Generalmente la página no es recargada después de que es abierta al principio, pero comunica la interacción del usuario con el servidor a través de comunicaciones AJAX. Una ventana en una aplicación AJAX es por lo tanto más como una ventana en una aplicación de escritorio y menos como una página web.

Window es el contenedor de nivel superior de una interfaz de usuario mostrado en una ventana de navegador. Cuando una aplicación AJAX normalmente se ejecuta en una sola "página" (URL), por lo general hay una sola ventana, la ventana principal. Se puede acceder a la ventana principal utilizando la dirección URL de la aplicación. Establezca la ventana principal con el método setMainWindow() de la clase Application.

import com.vaadin.ui.*;

public class HolaMundo extends com.vaadin.Application {
    public void init() { 
        Window principal = new Window("La Ventana Principal"); 
        setMainWindow(principal);

        ... llene la ventana principal con componentes ...     }
}

Puede agregar componentes a la ventana principal, o a cualquier otra ventana, con el método addComponent(), que en realidad añade el componente al componente de diseño raíz vinculado a la ventana. Si desea utilizar otro diseño raíz por defecto, puede establecerlo con setContent(), como se explica en la Sección 6.2, "Diseño Raiz Window y Panel".

Vaadin tiene dos tipos básicos de ventana: las ventanas de nivel de aplicación, tales como la ventana principal y las ventanas secundarias (o sub-ventanas) dentro de las ventanas de nivel de aplicación. Las ventanas secundarias se explican en la siguiente sección, mientras que las ventanas de nivel de aplicación se tratan en la Sección 12.2, "Ventanas de Nivel de Aplicación".



Anterior
Capítulo 4. Escribir una Aplicación Web
Siguiente
4.3. Ventanas Secundarias

Capítulo 4. Escribir una Aplicación Web

Este capítulo proporciona los fundamentos del desarrollo de aplicaciones web con Vaadin, concentrándose en los elementos básicos de una aplicación desde un punto de vista práctico.

Si eres un principiante en el desarrollo con AJAX, puede beneficiarse de la Sección 12.1, "Caracteristicas Especiales de Aplicaciones AJAX". En él se explica el rol de las páginas en aplicaciones web AJAX, y proporciona algunos modelos de diseño básico para las aplicaciones.

4.1. Información General

Una aplicación hecha con Vaadin se ejecuta como un Servlet Java en un contenedor de Servlets. El punto de entrada es la clase application, que necesita crear y administrar todos los componentes de la interfaz de usuario necesarios, incluyendo las ventanas. La interacción del usuario es manejada con oyentes de eventos, simplificado directamente por las vinculaciones de los componentes de interfaz de usuario a los datos. El aspecto visual es definido en temas como archivos CSS. Los iconos, otras imágenes, y archivos descargables son manejados como recursos, que pueden ser externos o servidos por el servidor de aplicaciones o por la propia aplicación.

Figura 4.1. Arquitectura de una Aplicación

La Figura 4.1, "Arquitectura de una Aplicación" arriba es la arquitectura básica de una aplicación hecha con el framework Vaadin, con todos los elementos principales, que se presentan a continuación y discutidos en detalle en este capítulo.

Primero que todo, una aplicación que utiliza Vaadin debe definir una clase application que hereda la clase abstracta com.vaadin.Application. La clase application debe implementar el método init().

public class MiApp extends com.vaadin.Application {

    public void init() { 
        ... el código de inicialización va aquí ...
    }
}

Además de actuar como el punto de entrada en el servlet, la clase Application proporciona facilidades para acceder a la ventana, el control de ejecución y la selección de temas. La API de application puede parecer similar a la API de Java Servlet, pero sólo es superficial. El framework Vaadin asocia las peticiones con sesiones, de modo que una instancia de la clase application es realmente un objeto de sesión. Debido a esto, puede desarrollar aplicaciones web, como lo haría con el desarrollo de aplicaciones de escritorio.

Reiniciar la Sesión de la Aplicación

Cuando abre la dirección URL de la aplicación, se crea una nueva sesión de usuario. La sesión se conserva incluso si vuelve a cargar la página. Sin embargo, ya que a Eclipse le gusta hacer el despliegue en caliente para Tomcat, y a Tomcat le gusta persistir sesiones en el cierre del servidor, usted puede experimentar un problema y es que la aplicación no vuelve a su estado inicial después de modificar el código o incluso reiniciar el servidor.

Añadiendo el parámetro ?restartApplication en la URL le indica al servlet Vaadin que cree una nueva instancia de Application cuando se recarge la página.

Lo más importante en la inicialización, es la creación de la ventana principal (véase más adelante), que cualquier aplicación tiene. Esto, y el despliegue de la aplicación como un Servlet Java en el contenedor de Servlets, tal como se describe en la Sección 4.8, "Configurar el Entorno de la Aplicación", son los requisitos mínimos para una aplicación.

A continuación se muestra una breve descripción de los elementos básicos de una aplicación:

Ventanas
Una aplicación siempre tiene una ventana principal, tal como se describe en la Sección 4.2, "Administrar la Ventana Principal". Una aplicación puede realmente tener varias ventanas de nivel de aplicación, todas unidas a la misma sesión de la aplicación, como se describe en la Sección 12.2, "Ventanas de Nivel de Aplicación". Las ventanas de nivel de aplicación pueden contener sub-ventanas no nativas, que son esencialmente los componentes de diseño flotante manejados dentro del navegador.

Componentes de Interfaz de Usuario
La interfaz de usuario se compone de los componentes de interfaz de usuario (UI) que son creados y presentados por la aplicación. La interacción del usuario con los componentes causa eventos (véase abajo) relacionados con el componente que la aplicación debe manejar. La mayor parte de los componentes están vinculados a algun dato utilizando el Modelo de Datos (véase abajo). Puede hacer sus propios componentes de interfaz de usuario ya sea a través de herencia o composición. Para una referencia completa de los componentes de interfaz de usuario, véase el Capítulo 5, Componentes de Interfaz de Usuario, para los componentes de diseño, vea el Capítulo 6, Admnistrar el Diseño, y para la composición de componentes, consulte la Sección 5.23 "Composición de Componentes con CustomComponent".

Eventos y Oyentes
Los eventos y oyentes que manejan los eventos, son la base del manejo de la interacción del usuario en una aplicación. La Sección 3.5, "Eventos y Oyentes" aportó una introducción a los eventos y oyentes desde un punto de vista arquitectónico, mientras que la Sección 4.4, "Controlar Eventos con Oyentes", más adelante este capítulo tiene una visión más práctica.

Recursos
Una interfaz de usuario puede mostrar imágenes o enlaces a páginas web o documentos descargables. Estos son recursos, que pueden ser externos o proporcionados por el servidor web o la propia aplicación. La Sección 4.5, "Referencias Recursos" proporciona una visión práctica de los diferentes tipos de recursos.

Temas (Estilos)
La presentación y la lógica de la interfaz de usuario están separadas. Mientras que la lógica de la interfaz de usuario es controlada como código de Java, la presentación es definida en temas como CSS. Vaadin proporciona un tema predeterminado. Los temas definidos por el usuario pueden, ademas de las hojas de estilo, incluir plantillas HTML que definen diseños personalizados y otros recursos temáticos, tales como imágenes. Los temas son discutidos en detalle en el Capítulo 8, Temas, los diseños personalizados en la Sección 6.13, "Diseños Personalizados", y los recursos de temas en la Sección 4.5.4, "Recursos de Temas".

Vincular Datos
Los campos de los componentes son esencialmente vistas a datos, representados en un modelo de datos. Utilizando el modelo de datos, los componentes pueden actualizar los datos de la aplicación directamente, sin la necesidad de ningún código de control. Un modelo de componentes de un campo siempre está vinculado a una propiedad, un elemento, o un contenedor, dependiendo del tipo de campo. Si bien todos los componentes tienen un modelo de datos por defecto, que pueden vincularse a un origen de datos definido por el usuario. Por ejemplo, puede vincular un componente table a la respuesta de una consulta SQL. Para una descripción completa de vinculación de datos en Vaadin, por favor, consulte el Capítulo 9, "Vincular Componentes a Datos".



Anterior
3.5. Eventos y Oyentes
Siguiente
4.2. Administrar la Ventana Principal

3.5. Eventos y Oyentes

Vaadin ofrece un modelo de programación orientada a eventos para el manejo de la interacción del usuario. Cuando un usuario hace algo, como hacer clic en un botón o seleccionar un elemento, la aplicación necesita tener conocimiento de esto. Muchos frameworks basados en interfaz de usuario Java siguen el modelo Evento-Oyente (conocido tambien como el modelo de diseño Observador) para comunicar la entrada de datos por el usuario a la lógica de la aplicación. Vaadin también lo hace. El modelo de diseño implica dos clases de elementos: un objeto que genera ("dispara" o "emite") eventos y un número de oyentes que escuchan los eventos. Cuando ocurre un evento, el objeto envía una notificación de ello a todos los oyentes. En un caso típico, sólo hay un oyente.

Los eventos pueden servir para muchos propósitos. En Vaadin, el propósito habitual de los eventos es manejar la interacción del usuario en una interfaz de usuario. La administración de sesiones puede requerir eventos especiales, tales como el tiempo-muerto, cuyo caso el evento es en realidad la falta de interacción del usuario. El tiempo-muerto es un caso especial de eventos programados o previstos, donde un evento ocurre en una fecha y hora específica o cuando se ha cumplido una hora. Las bases de datos y otros tipos de comunicación asincrónica también pueden causar eventos.

Para recibir eventos de un tipo en particular, una aplicación debe registrar un objeto oyente con el origen del evento. Los oyentes son registrados en los componentes con el addListener(). El método tiene una versión genérica definida en el nivel de AbstractComponent, la clase base de todos los componentes.

La mayoría de los componentes que tienen eventos relacionados definen su propia clase de evento y sus respectivas clases oyentes. Por ejemplo, Button tiene eventos Button.ClickEvent, el cual puede ser escuchado a travéz de la interfaz Button.ClickListener.

A continuación, manejamos los clics de button con un oyente implementado como una clase anónima:

final Button button = new Button("Presionalo!");

button.addListener(new Button.ClickListener() {
    public void buttonClick(ClickEvent event) {
        button.setCaption("Lo presionaste!");
    }
});

La Figura 3.3, "Diagrama de Clases de un Oyente de Clic de Button" ilustra un ejemplo donde una clase específica de la aplicación hereda la interfaz Button.ClickListener capaz de escuchar eventos clic de button. La aplicación debe crear instancias de la clase oyente y registrarla con addListener(). Cuando ocurre un evento, el evento de un objeto es instanciado, en este caso un ClickEvent. El evento del objeto conoce el componente de interfaz de usuario relacionado, en este caso Button.

Figura 3.3. Diagrama de Clases de un Oyente de Clic de Button

En los viejos tiempos de programación en C, las retrollamadas a funciones llenaron en gran parte la misma necesidad como los oyentes lo hacen ahora. En lenguajes orientados a objetos, sólo tenemos clases y métodos, no funciones, por lo que la aplicación tiene que proporcionar una clase interface en lugar de un puntero a una retrollamada de función al framework. Sin embargo, Vaadin también admite la definición de un método como un oyente, utilizando el wrapper MethodListener.

Tenga en cuenta que muchas interfaces oyentes heredan de la superinterface java.util.EventListener, pero generalmente no es necesario heredarlo.

La Sección 4.4, "Controlar Eventos con Oyentes" explica en detalle como manejar eventos en la práctica.



Anterior
3.4. Motor del Lado del Cliente
Siguiente
Capítulo 4. Escribir una Aplicación Web