Tapiz Apache - Componentes

Como se mencionó anteriormente, los componentes y las páginas son iguales, excepto que la página es el componente raíz e incluye uno o más componentes secundarios. Los componentes siempre residen dentro de una página y hacen casi toda la funcionalidad dinámica de la página.

Los componentes de tapiz representan enlaces HTML simples a la funcionalidad de cuadrícula compleja con interactive AJAX. Un componente también puede incluir otro componente. Los componentes del tapiz constan de los siguientes elementos:

  • Component Class - La clase principal de Java del componente.

  • XML Template- La plantilla XML es similar a la plantilla de página. La clase de componente representa la plantilla como resultado final. Es posible que algunos componentes no tengan plantillas. En este caso, la salida será generada por la propia clase de componente usando elMarkupWriter clase.

  • Body- El componente especificado dentro de la plantilla de página puede tener un marcado personalizado y se denomina "Cuerpo del componente". Si la plantilla de componente tiene<body />, entonces el elemento <body /> será reemplazado por el cuerpo del componente. Esto es similar al diseño que se discutió anteriormente en la sección de plantillas XML.

  • Rendering - La representación es un proceso que transforma la plantilla XML y el cuerpo del componente en una salida real del componente.

  • Parameters - Se utiliza para crear comunicación entre componentes y páginas y, por lo tanto, pasar datos entre ellos.

  • Events- Delega la funcionalidad de los componentes a su contenedor / padre (páginas u otro componente). Se utiliza ampliamente en la navegación de páginas.

Representación

La renderización de un componente se realiza en una serie de fases predefinidas. Cada fase del sistema de componentes debe tener un método correspondiente definido por convención o anotación en la clase de componente.

// Using annotaion 
@SetupRender 
void initializeValues() { 
   // initialize values 
}

// using convention 
boolean afterRender() { 
   // do logic 
   return true; 
}

Las fases, su nombre de método y sus anotaciones se enumeran a continuación.

Anotación Nombres de métodos predeterminados
@SetupRender setupRender ()
@BeginRender beginRender ()
@BeforeRenderTemplate beforeRenderTemplate ()
@BeforeRenderBody beforeRenderBody ()
@AfterRenderBody afterRenderBody ()
@AfterRenderTemplate afterRenderTemplate ()
@AfterRender afterRender ()
@CleanupRender cleanupRender ()

Cada fase tiene un propósito específico y son los siguientes:

SetupRender

SetupRender inicia el proceso de renderizado. Suele configurar los parámetros del componente.

BeginRender

BeginRender comienza a renderizar el componente. Por lo general, muestra la etiqueta de inicio / inicio del componente.

BeforeRenderTemplate

BeforeRenderTemplate se usa para decorar la plantilla XML, agregando un marcado especial alrededor de la plantilla. También proporciona una opción para omitir la representación de la plantilla.

BeforeRenderBody

BeforeRenderTemplate proporciona una opción para omitir la representación del elemento del cuerpo del componente.

AfterRenderBody

AfterRenderBody se llamará después de que se procese el cuerpo del componente.

AfterRenderTemplate

AfterRenderTemplate se llamará después de que se procese la plantilla del componente.

AfterRender

AfterRender es la contraparte de BeginRender y normalmente muestra la etiqueta de cierre.

CleanupRender

CleanupRender es la contraparte de SetupRender. Libera / elimina todos los objetos creados durante el proceso de renderizado.

El flujo de las fases de renderizado no es solo hacia adelante. Va y viene entre fases dependiendo del valor de retorno de una fase.

Por ejemplo, si el método SetupRender devuelve falso, la renderización salta a la fase CleanupRender y viceversa. Para encontrar una comprensión clara del flujo entre las diferentes fases, verifique el flujo en el diagrama que se muestra a continuación.

Componente simple

Creemos un componente simple, Hello, que tendrá el mensaje de salida como "Hello, Tapestry". A continuación se muestra el código del componente Hello y su plantilla.

package com.example.MyFirstApplication.components;  
public class Hello {  
}
<html  
   xmlns:t = "https://tapestry.apache.org/schema/tapestry_5_4.xsd" 
   xmlns:p = "tapestry:parameter"> 
  
   <div> 
      <p>Hello, Tapestry (from component).</p> 
   </div> 
  
</html>

El componente Hello se puede llamar en una plantilla de página como:

<html title = "Hello component test page" 
   xmlns:t = "https://tapestry.apache.org/schema/tapestry_5_4.xsd" 
   xmlns:p = "tapestry:parameter"> 
<t:hello />  
</html>

De manera similar, el componente puede generar la misma salida usando MarkupWriter en lugar de la plantilla como se muestra a continuación.

package com.example.MyFirstApplication.components; 
  
import org.apache.tapestry5.MarkupWriter; 
import org.apache.tapestry5.annotations.BeginRender;   

public class Hello { 
   @BeginRender 
   void renderMessage(MarkupWriter writer) { 
      writer.write("<p>Hello, Tapestry (from component)</p>"); 
   } 
}

Cambiemos la plantilla del componente e incluyamos el elemento <body /> como se muestra en el bloque de código a continuación.

<html>  
   xmlns:t = "https://tapestry.apache.org/schema/tapestry_5_4.xsd" 
   xmlns:p = "tapestry:parameter"> 
   
   <div> 
      <t:body /> 
   </div> 
</html>

Ahora, la plantilla de página puede incluir el cuerpo en el marcado del componente como se muestra a continuación.

<html title = "Hello component test page" 
   xmlns:t = "https://tapestry.apache.org/schema/tapestry_5_4.xsd" 
   xmlns:p = "tapestry:parameter"> 
   
   <t:hello> 
      <p>Hello, Tapestry (from page).</p> 
   </t:hello> 
</html>

La salida será la siguiente:

<html> 
   <div> 
      <p>Hello, Tapestry (from page).</p> 
   </div> 
</html>

Parámetros

El propósito principal de estos parámetros es crear una conexión entre un campo del componente y una propiedad / recurso de la página. Usando parámetros, el componente y su página correspondiente se comunican y transfieren datos entre sí. Se llamaTwo Way Data Binding.

Por ejemplo, un componente de cuadro de texto utilizado para representar la edad en una página de administración de usuarios obtiene su valor inicial (disponible en la base de datos) a través del parámetro. Nuevamente, después de que la edad del usuario se actualice y se envíe de vuelta, el componente enviará la edad actualizada a través del mismo parámetro.

Para crear un nuevo parámetro en la clase de componente, declare un campo y especifique un @Parameteranotación. Este @Parameter tiene dos argumentos opcionales, que son:

  • required- hace que el parámetro sea obligatorio. Tapestry genera una excepción si no se proporciona.

  • value : Especifica el valor predeterminado del parámetro.

El parámetro debe especificarse en la plantilla de página como atributos de la etiqueta del componente. El valor de los atributos debe especificarse mediante Binding Expression / Expansion, que discutimos en los capítulos anteriores. Algunas de las expansiones que aprendimos anteriormente son:

  • Property expansion (prop:«val») - Obtener los datos de la propiedad de la clase de página.

  • Message expansion (message:«val») - Obtener los datos de la clave definida en el archivo index.properties.

  • Context expansion (context:«val») - Obtenga los datos de la carpeta de contexto web / src / main / webapp.

  • Asset expansion (asset:«val») - Obtenga los datos de los recursos incrustados en el archivo jar, / META-INF / assets.

  • Symbol expansion (symbol:«val») - Obtenga los datos de los símbolos definidos en AppModule.javafile.

Tapestry tiene muchas más expansiones útiles, algunas de las cuales se detallan a continuación:

  • Literal expansion (literal:«val») - Una cadena literal.

  • Var expansion (var:«val») - Permitir leer o actualizar una variable de renderizado del componente.

  • Validate expansion (validate:«val»)- Una cadena especializada que se usa para especificar la regla de validación de un objeto. Por ejemplo, validate: required, minLength = 5.

  • Translate (translate:«val») - Se utiliza para especificar la clase de traductor (conversión del lado del cliente a la representación del lado del servidor) en la validación de entrada.

  • Block (block:«val») - La identificación del elemento de bloque dentro de la plantilla.

  • Component (component:«val») - La identificación del otro componente dentro de la plantilla.

Todas las expansiones anteriores son de solo lectura, excepto la expansión Propiedad y la expansión Var. El componente los utiliza para intercambiar datos con la página. Al utilizar la expansión como valores de atributo,${...}No debería ser usado. En su lugar, utilice la expansión sin símbolos de dólar y llaves.

Componente con parámetro

Creemos un nuevo componente, HelloWithParameter modificando el componente Hello para representar dinámicamente el mensaje agregando un name en la clase de componente y cambiando la plantilla de componente y la plantilla de página en consecuencia.

  • Crea una nueva clase de componente HelloWithParameter.java.

  • Agregue un campo privado y asígnele el nombre @Parameteranotación. Utilice el argumento requerido para que sea obligatorio.

@Parameter(required = true) 
private String name;
  • Agregue un campo privado, resultado con @Properyanotación. La propiedad del resultado se utilizará en la plantilla del componente. La plantilla de componente no tiene acceso a los campos anotados con@Parameter y solo puede acceder a los campos anotados con @Property. Las variables disponibles en las plantillas de componentes se denominan Variables de renderizado.

@Property 
 private String result;
  • Agregue un método RenderBody y copie el valor del parámetro de nombre a la propiedad de resultado.

@BeginRender 
void initializeValues() { 
   result = name; 
}
  • Agregar una nueva plantilla de componente HelloWithParamter.tml y use la propiedad de resultado para representar el mensaje.

<div> Hello, ${result} </div>
  • Agregue una nueva propiedad, Nombre de usuario en la página de prueba (testhello.java).

public String getUsername() { 
   return "User1"; 
}
  • Utilice el componente recién creado en la plantilla de página y establezca la propiedad Nombre de usuario en el parámetro de nombre de HelloWithParameter componente.

<t:helloWithParameter name = "username" />

La lista completa es la siguiente:

package com.example.MyFirstApplication.components;  

import org.apache.tapestry5.annotations.*;  
public class HelloWithParameter { 
   @Parameter(required = true) 
   private String name; 
     
   @Property 
   private String result; 
   
   @BeginRender 
   void initializeValues() { 
      result = name; 
   } 
}
<html  
   xmlns:t = "https://tapestry.apache.org/schema/tapestry_5_4.xsd" 
   xmlns:p = "tapestry:parameter"> 
   
   <div> Hello, ${result} </div> 
  
</html>
package com.example.MyFirstApplication.pages;  

import org.apache.tapestry5.annotations.*;  
public class TestHello { 
   public String getUsername() { 
      return "User1"; 
   } 
}
<html title = "Hello component test page" 
   xmlns:t = "https://tapestry.apache.org/schema/tapestry_5_4.xsd" 
   xmlns:p = "tapestry:parameter"> 
   <t:helloWithParameter name = "username" />
   
</html>

El resultado será el siguiente:

<div> Hello, User1 </div>

Parámetro avanzado

En los capítulos anteriores, analizamos cómo crear y usar un parámetro simple en un componente personalizado. Un parámetro avanzado también puede contener un marcado completo. En este caso, el marcado debe especificarse dentro de la etiqueta del componente, como la subsección en la plantilla de página. El componente if incorporado tiene marcado tanto para condiciones de éxito como de falla. El marcado de éxito se especifica como el cuerpo de la etiqueta del componente y el marcado de error se especifica mediante unelseparameter.

Veamos cómo usar el ifcomponente. El componente if tiene dos parámetros:

  • test - Parámetro simple basado en propiedades.

  • Else - Parámetro avanzado utilizado para especificar un marcado alternativo, si la condición falla

Tapestry verificará el valor de la propiedad de prueba usando la siguiente lógica y devolverá verdadero o falso. Se llamaType Coercion, una forma de convertir un objeto de un tipo en otro tipo con el mismo contenido.

  • Si el tipo de datos es String, "Verdadero" si no está en blanco y no es la cadena literal "Falso" (no distingue entre mayúsculas y minúsculas).

  • Si el tipo de datos es Number, Verdadero si no es cero.

  • Si el tipo de datos es Collection, Verdadero si no está vacío.

  • Si el tipo de datos es Object, Verdadero (siempre que no sea nulo).

Si la condición pasa, el componente renderiza su cuerpo; de lo contrario, representa el cuerpo del parámetro else.

La lista completa es la siguiente:

package com.example.MyFirstApplication.pages; 
public class TestIf { 
   public String getUser() { 
      return "User1"; 
   } 
}

<html title = "If Test Page" 
   xmlns:t = "http://tapestry.apache.org/schema/tapestry_5_4.xsd" 
   xmlns:p = "tapestry:parameter">  
   
   <body> 
      <h1>Welcome!</h1>  
      <t:if test = "user"> 
         Welcome back, ${user} 
         <p:else>
            Please <t:pagelink page = "login">Login</t:pagelink>  
         </p:else> 
      </t:if>
   </body>
   
</html>

Eventos de componentes / navegación de página

La aplicación de tapiz es una collection of Pagesinteractuando entre sí. Hasta ahora, hemos aprendido a crear páginas individuales sin ninguna comunicación entre ellas. El propósito principal de un evento de componente es proporcionar interacción entre páginas (también dentro de las páginas) utilizando eventos del lado del servidor. La mayoría de los eventos componentes se originan en eventos del lado del cliente.

Por ejemplo, cuando un usuario hace clic en un enlace en una página, Tapestry llamará a la misma página con la información de destino en lugar de llamar a la página de destino y generará un evento del lado del servidor. La página de tapiz capturará el evento, procesará la información del objetivo y hará una redirección del lado del servidor a la página de destino.

Tapiz sigue un Post/Redirect/Get (RPG) design patternpara navegar por la página. En RPG, cuando un usuario realiza una solicitud de publicación al enviar un formulario, el servidor procesará los datos publicados, pero no devolverá la respuesta directamente. En su lugar, hará una redirección del lado del cliente a otra página, que generará el resultado. Se utiliza un patrón de RPG para evitar envíos de formularios duplicados a través del botón de retroceso del navegador, el botón de actualización del navegador, etc. Tapestry proporciona un patrón de RPG al proporcionar los siguientes dos tipos de solicitud.

  • Component Event Request- Este tipo de solicitud se dirige a un componente particular de una página y genera eventos dentro del componente. Esta solicitud solo realiza una redirección y no genera la respuesta.

  • Render Request - Estos tipos de solicitudes se dirigen a una página y transmiten la respuesta al cliente.

Para comprender los eventos del componente y la navegación de la página, necesitamos conocer el patrón de URL de la solicitud de tapiz. El patrón de URL para ambos tipos de solicitud es el siguiente:

  • Component Event Requests -

/<<page_name_with_path>>.<<component_id|event_id>>/<<context_information>>
  • Render Request -

/<<page_name_with_path>>/<<context_information>>

Algunos de los ejemplos de patrones de URL son:

  • La página de índice puede ser solicitada por https://«domain»/«app»/index.

  • Si la página de índice está disponible en una subcarpeta admin, entonces puede ser solicitada por https://«domain»/«app»/admin/index.

  • Si el usuario hace clic en el ActionLink component con id test en la página de índice, entonces la URL será https://«domain»/«app»/index.test.

Eventos

Por defecto, Tapestry sube OnPassivate y OnActivateeventos para todas las solicitudes. Para el tipo de solicitud de evento de componente, tapestry genera uno o más eventos adicionales según el componente. El componente ActionLink genera un evento Action, mientras que un componente Form genera varios eventos comoValidate, Success, etc.,

Los eventos se pueden manejar en la clase de página usando el manejador de método correspondiente. El controlador de métodos se crea mediante una convención de nomenclatura de métodos o mediante el@OnEventanotación. El formato de la convención de nomenclatura de métodos esOn«EventName»From«ComponentId».

Un evento de acción del componente ActionLink con id test se puede manejar mediante uno de los siguientes métodos:

void OnActionFromTest() { 
}  
@OnEvent(component = "test", name = "action") 
void CustomFunctionName() { 
}

Si el nombre del método no tiene ningún componente en particular, entonces se llamará al método para todos los componentes con eventos coincidentes.

void OnAction() { 
}

Evento OnPassivate y OnActivate

OnPassivate se utiliza para proporcionar información de contexto para un controlador de eventos OnActivate. En general, Tapestry proporciona la información de contexto y se puede utilizar como argumento en el controlador OnActivateevent.

Por ejemplo, si la información de contexto es 3 de tipo int, entonces el evento OnActivate se puede llamar como -

void OnActivate(int id) { 
}

En algún escenario, es posible que la información de contexto no esté disponible. En esta situación, podemos proporcionar la información de contexto al controlador de eventos OnActivate a través del controlador de eventos OnPassivate. El tipo de retorno del controlador de eventos OnPassivate debe usarse como argumento del controlador de eventos OnActivate.

int OnPassivate() { 
   int id = 3; 
   return id; 
} 
void OnActivate(int id) { 
}

Valores de retorno del controlador de eventos

Tapestry emite la redirección de la página en función de los valores de retorno del controlador de eventos. El controlador de eventos debe devolver cualquiera de los siguientes valores.

  • Null Response- Devuelve un valor nulo. Tapestry construirá la URL de la página actual y la enviará al cliente como redireccionamiento.

public Object onAction() { 
   return null; 
}
  • String Response- Devuelve el valor de la cadena. Tapestry construirá la URL de la página que coincida con el valor y la enviará al cliente como redireccionamiento.

public String onAction() { 
   return "Index"; 
}
  • Class Response- Devuelve una clase de página. Tapestry construirá la URL de la clase de página devuelta y la enviará al cliente como redireccionamiento.

public Object onAction() { 
   return Index.class 
}
  • Page Response- Devuelve un campo anotado con @InjectPage. Tapestry construirá la URL de la página inyectada y la enviará al cliente como redireccionamiento.

@InjectPage 
private Index index;  

public Object onAction(){ 
   return index; 
}
  • HttpError- Devuelve el objeto HTTPError. Tapestry emitirá un error HTTP del lado del cliente.

public Object onAction(){ 
   return new HttpError(302, "The Error message); 
}
  • Link Response- Devuelve una instancia de enlace directamente. Tapestry construirá la URL a partir del objeto Link y la enviará al cliente como redireccionamiento.

  • Stream Response - Devuelve el StreamResponseobjeto. Tapestry enviará la transmisión como respuesta directamente al navegador del cliente. Se utiliza para generar informes e imágenes directamente y enviarlo al cliente.

  • Url Response - Devuelve el java.net.URLobjeto. Tapestry obtendrá la URL correspondiente del objeto y la enviará al cliente como redireccionamiento.

  • Object Response- Devuelve cualquier valor distinto de los valores especificados anteriormente. Tapiz generará un error.

Contexto del evento

En general, el controlador de eventos puede obtener la información de contexto mediante argumentos. Por ejemplo, si la información de contexto es 3 de tipo int, entonces el controlador de eventos será:

Object onActionFromTest(int id) {  
}

Tapestry maneja adecuadamente la información de contexto y la proporciona a los métodos a través de argumentos. A veces, es posible que Tapestry no pueda manejarlo correctamente debido a la complejidad de la programación. En ese momento, podemos obtener la información de contexto completa y procesarla nosotros mismos.

Object onActionFromEdit(EventContext context) { 
   if (context.getCount() > 0) { 
      this.selectedId = context.get(0); 
   } else { 
      alertManager.warn("Please select a document."); 
      return null; 
   } 
}