Doble barra de botones con Apache Tapestry

Escrito por el , actualizado el .
java programacion tapestry planeta-codigo
Enlace permanente Comentarios

Apache Tapestry

Por motivos de usabilidad en algunas páginas se incluyen dos barra de botones, una antes de una larga sección y otra después. El motivo de la doble barra de botones inicial es que no sea necesario hacer «scroll» hasta el final de la página para acceder a los botones y realizar la acción que permitan. Por el contrario, incluir la barra de botones al final de la página permite que una vez seleccionados los elementos o revisado el contenido de la sección hacer disponibles las acciones que es probable que se quieran realizar.

Doble barra de botones

Dependiendo del framework web que utilicemos podremos hacerlo de una o varias formas pero de lo que estamos seguros es que copiar y pegar haciendo que el código esté duplicado no es una buena idea por los problemas de mantenimiento que puede suponer. Pero crear una pequeña plantilla o archivo exclusivo para incluir la barra de botones tampoco es la solución ideal, ¿por que? pues porque creando un archivo específico con la botonera puede que nos ocasione un problema que denominaré de «microgestión», es decir, nos obliga a crear un montón de pequeños archivos pequeñitos y hacer referencia o utilizar el mecanismo de inclusión que dispongamos para usar el contenido en unos de otros. En una aplicación grande esta microgestión si nos vemos obligados a ella puede llegar a ser molesta al desarrollar cuanto menos.

¿Como se puede evitar? En el framework Apache Tapestry la doble botonera puede hacerse de varias formas una de ellas es crear un componente pero esto nos obliga a crear un archivo para la clase java y probablemente también un archivo de plantilla con el contenido html causando el problema de la microgestión. Pero en Tapestry también podemos hacer uso del componente block que sirve para incluir en él cierto contenido y el componente delegate que sirve para emitir el contenido entre otras cosas de un componente block. Usando estos dos componentes podemos evitar la microgestión definiendo todo en un mismo archivo, además al tener todo en un mismo archivo el código será más fácilmente legible, quedándonos en un ejemplo de la siguiente forma:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
<!DOCTYPE html>
<html t:type="layout" titulo="Administración de productos" xmlns:t="http://tapestry.apache.org/schema/tapestry_5_3.xsd" xmlns:p="tapestry:parameter">

...

<t:block id="edicionBlock">
        <t:remove>
        En otros frameworks la lógica para obtener el título del bloque según se trate de un alta o una modificación,
        probablemente se hiciese metiendo lógica en la plantilla de presentación, dado que Tapestry permite llamar a métodos
        de la clase Java asociada al componente es mejor dejar esa lógica en el código Java de esta manera la plantilla será más 
        sencilla y clara además de aprovecharnos del compilador. labels es un método definido en la página admin.producto
        que devuelve un mapa.
        </t:remove>
	<h1>${labels.get('titulo')}</h1>

	<t:form t:id="form" context="producto.id" validate="producto" clientValidation="none" class="well" role="form">
		<t:errors class="literal:alert alert-danger" />

		<t:delegate to="botonesEdicionBlock"/>

		<div style="margin-top: 10px;">
			<div class="form-group">
				<t:label for="nombre" />
				<div class="controls">
					<input t:type="textfield" t:id="nombre" value="producto.nombre" size="100" label="Nombre" />
				</div>
			</div>
	
			<div class="form-group">
				<t:label for="descripcion" />
				<div class="controls">
					<input t:type="textarea" t:id="descripcion" value="producto.descripcion" label="Descripción" />
				</div>
			</div>
	
			<div class="form-group">
				<t:label for="cantidad" />
				<div class="controls">
					<input t:type="textfield" t:id="cantidad" value="producto.cantidad" size="4" label="Cantidad" class="numeric"/>
				</div>
			</div>
	
			<div class="form-group">
				<t:label for="fecha" />
				<div class="controls">
					<div class="input-group">
						<input t:type="textfield" t:id="fecha" type="date" value="producto.fecha" label="Fecha" />
						<span class="input-group-addon"><span class="glyphicon glyphicon-calendar"></span></span>
					</div>				
				</div>
			</div>
		</div>

		<t:delegate to="botonesEdicionBlock"/>
	</t:form>
</t:block>

<t:block id="botonesEdicionBlock">
	<div class="btn-toolbar">
		<input t:type="submit" class="btn btn-primary" value="prop:labels.get('guardar')" role="button"/>
		<t:if test="producto.id">
			<a t:type="eventlink" event="eliminar" context="producto.id" role="button" class="btn btn-danger" style="color: white;">Eliminar</a>
		</t:if>
		<input t:type="submit" class="btn btn-danger" value="Cancelar" mode="cancel" role="button"/>
	</div>
</t:block>

</html>
ProductoAdmin.tml

En Grails por poner un ejemplo de un framework que no usa el concepto de componentes la forma habitual de hacerlo es usando un g:include y con ello teniendo microgestión. Pero retorciendo un poco (creo) en este caso el uso de Grails podemos emplear la etiqueta g:set para establecer el contenido de la botonera y emitir su contenido dos veces en el gsp.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
...

<g:set var="botonesEdicionBlock">
	<div class="btn-toolbar">
		<input type="submit" class="btn btn-primary" value="${message(code: 'guardar')}" role="button"/>
		<g:if test="${producto.id}">
			<g:link action="eliminar" params="${[id: producto.id]}" role="button" class="btn btn-danger" style="color: white;">Eliminar</a>
		</t:if>
		<input type="submit" class="btn btn-danger" value="{message(code: 'guardar')}" role="button"/>
	</div>
</g:set>

<g:form ...>
	...

	${botonesEdicionBlock}

	<div style="margin-top: 10px;">
		...
	</div>

	${botonesEdicionBlock}
</g:form>
ProductoAdmin.gsp

El código completo de este ejemplo del caso de Tapestry está en un repositorio de GitHub. Si estás interesado en conocer más en profundidad como funciona Tapestry y sus múltiples «killer features», bastantes mucho más importantes que lo explicado en este artículo, puedes descargarte el libro PlugIn Tapestry que he escrito, de forma gratuita, sin registros, y en varios formatos ¿que más puedes pedir?. Y si te interesa el tema puedes suscribirte al canal RSS de esta bitácora para no perderte nada del nuevo contenido que publique, no solo sobre Tapestry, sino también sobre Java, Linux, …

Portada libro: PlugIn Tapestry

Libro PlugIn Tapestry

Si te interesa Apache Tapestry descarga gratis el libro de más de 300 páginas que he escrito sobre este framework en el formato que prefieras, PlugIn Tapestry: Desarrollo de aplicaciones y páginas web con Apache Tapestry, y el código de ejemplo asociado. En el libro comento detalladamente muchos aspectos que son necesarios en una aplicación web como persistencia, pruebas unitarias y de integración, inicio rápido, seguridad, formularios, internacionalización (i18n) y localización (l10n), AJAX, ... y como abordarlos usando Apache Tapestry.



Comparte el artículo: