Plantillas en Apache Tapestry

Escrito por el , actualizado el .
java programacion tapestry planeta-codigo blog-stack
Comentarios

Apache Tapestry

Una página web está formada por un conjunto de páginas enlazadas entre ellas. Cada página está formado por un html diferente pero normalmente todas las páginas de una misma web comparten el mismo aspecto variando solo una sección donde está el contenido propio de la página. La cabecera de la página, el pie de la página o los menús de navegación suelen estar presentes en todas las páginas de la web y suelen ser los mismos.

En este artículo voy a explicar como crear un componente que nos de a todas las páginas un aspecto común de una aplicación usando apache Tapestry como framework web de tal forma que esa parte común no esté duplicada en la aplicación y pueda ser reutilizada fácilmente. En el caso de Blog Stack las páginas se componen de las siguientes partes.

El esquema de la plantilla será una cabecera, una barra de navegación con enlaces a diferentes secciones de la web, un menú lateral con contenido variable según la página, el contenido que variará según la página y un pie de página. Como todo componente de Apache Tapestry está formado de una clase Java y una plantilla. El componente puede tener diferentes parámetros, y en el caso del de la plantilla muchos para poder variar el contenido por defecto de las diferentes secciones de la página, estos son aside1, aside2, aside3, aside4.

 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
69
70
71
72
73
74
75
76
77
78
package info.blogstack.components;

import info.blogstack.entities.Adsense;

import org.apache.tapestry5.BindingConstants;
import org.apache.tapestry5.Block;
import org.apache.tapestry5.ComponentResources;
import org.apache.tapestry5.annotations.Import;
import org.apache.tapestry5.annotations.Parameter;
import org.apache.tapestry5.annotations.Property;
import org.apache.tapestry5.ioc.annotations.Inject;
import org.joda.time.DateTime;

@Import(stack = "blogstack", module = "app/analytics")
public class Layout {

    @Parameter(defaultPrefix = BindingConstants.LITERAL)
    @Property(read = false)
    private String title;
    
    @Parameter(defaultPrefix = BindingConstants.LITERAL)
    @Property(read = false)
    private String subtitle;
    
    @Parameter(defaultPrefix = BindingConstants.BLOCK)
    @Property
    private Block aside1;
    
    @Parameter(defaultPrefix = BindingConstants.BLOCK)
    @Property
    private Block aside2;
    
    @Parameter(defaultPrefix = BindingConstants.BLOCK)
    @Property
    private Block aside3;
    
    @Parameter(defaultPrefix = BindingConstants.BLOCK)
    @Property
    private Block aside4;
    
    @Parameter
    @Property
    private Adsense adsense;

    @Property
    private String page;   
    
    @Inject
    ComponentResources resources;

    void setupRender() {
        page = resources.getPageName();
    }
    
    public int getYear() {
        return DateTime.now().getYear();
    }
    
    public String getTitle() {
        if (title == null) {
            return String.format("%s", getSubtitle());          
        } else {
            return String.format("%s | %s", title, getSubtitle());
        }
    }
    
    public String getSubtitle() {
        return (subtitle == null) ? "Blog Stack" : subtitle; 
    }
    
    public String getContentClass() {
        return (isAside()) ? "col-xs-12 col-sm-12 col-md-8 content" : "col-xs-12 col-sm-12 col-md-12 content";
    }
    
    public boolean isAside() {
        return (aside1 != null || aside2 != null || aside3 != null || aside4 != null);
    }
}

El archivo tml asociado al componente plantilla será el que genere el contenido html que se enviará al navegador del usuario. En esta plantilla se incluye una cabecera con el logo de la aplicación y una frase que lo describe, posteriormente está una barra de navegación con varios enlaces, con se incluye el contenido propio de la página que usa el componente plantilla y usando el componente se incluye el contenido de los diferentes bloques aside si se han personalizado en el uso de la plantilla, con el componente se comprueba si hay algún aside usándose el método isAside de la clase Layout asociada al componente plantilla y del tml. Finalmente, está el pie que será común a todas las páginas que usen este componente.

 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
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:t="http://tapestry.apache.org/schema/tapestry_5_4.xsd" xmlns:p="tapestry:parameter">
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
    <meta t:type="any" t:pagina="${page}" />
    <title>${title}</title>
    <!-- Resources -->
    <link href="//fonts.googleapis.com/css?family=Open+Sans:400,700" rel="stylesheet" type="text/css"/>
    <link href="//netdna.bootstrapcdn.com/font-awesome/4.0.3/css/font-awesome.css" rel="stylesheet" type="text/css"/> 
    <link href="/feed.atom.xml" rel="alternate" type="application/atom+xml" title="Portada"/>
    <link href="${context:images/favicon.png}" rel="icon" type="image/png"/>
</head>
<body>
    <header>
        <div class="container-fluid">
            <div class="row">
                <div class="col-xs-12 col-sm-12 col-md-4">
                    <h1><a t:type="pagelink" page="index" class="blogstack"><span class="glyphicon glyphicon-th"></span> Blog <span class="stack">Stack</span></a></h1>
                </div>
                <div id="horizontalSkycraper" class="col-xs-12 col-sm-12 col-md-8"></div>
            </div>
            <div class="row">
                <div  class="col-xs-12 col-sm-12 col-md-12">
                    <h4>Un poco más que un agregador/planeta de bitácoras sobre programación, desarrollo, software libre, gnu/linux, tecnología, ...</h4>
                </div>
            </div>
        </div>
    </header>

    <div class="container-fluid">
        <div class="row">
            <div class="col-xs-12 col-sm-12 col-md-12">
                <nav role="navigation">
                    <ul class="nav nav-pills menu">
                        <li><a t:type="pagelink" page="index">Inicio</a></li>
                        <li><a t:type="pagelink" page="archive" context="[]">Archivo</a></li>
                        <li><a t:type="pagelink" page="faq">Preguntas frecuentes</a></li>
                    </ul>
                </nav>
            </div>
        </div>
    </div>

    <div class="container-fluid">
        <div class="row">
            <div t:type="any" class="prop:contentClass"><t:body /></div>
            <t:if test="aside">
                <aside class="col-xs-12 col-sm-12 col-md-4">
                    <t:socialnetworks/>
                    <t:if test="aside1">
                        <t:delegate to="aside1"/>
                    </t:if>
                    <div id="bigRectangle"></div>
                    <t:if test="aside2">
                        <t:delegate to="aside2"/>
                    </t:if>
                    <div class="row">
                        <div class="col-xs-3 col-md-2">
                            <div id="wideSkycraper"></div>
                        </div>
                        <t:if test="aside3">
                            <div class="col-xs-3 col-md-2">
                                <t:delegate to="aside3"/>
                            </div>
                        </t:if>
                    </div>
                    <t:if test="aside4">
                        <t:delegate to="aside4"/>
                    </t:if>
                </aside>
            </t:if>
        </div>
    </div>

    <footer>
        <div class="container-fluid">
            <div class="row">
                <div class="col-xs-12 col-sm-12 col-md-12">
                    <div class="footer">
                        <a t:type="pagelink" page="index">Blog Stack</a> por <a href="https://twitter.com/picodotdev/">pico.dev</a> está publicado bajo la licencia de software libre <a href="http://www.gnu.org/licenses/agpl-3.0.html">GNU Affero General Public</a>.<br/>
                        El contenido agregado conserva la licencia de su bitácora.<br/>
                        «Powered by» <a href="https://github.com/picodotdev/blogstack">Blog Stack</a>, <a href="http://tapestry.apache.org/">Apache Tapestry</a>, <a href="https://www.openshift.com/">OpenShift</a>, <a href="https://pages.github.com/">GitHub Pages</a>, <a href="http://www.oracle.com/es/technologies/java/overview/index.html">Java</a> y más software libre o de código abierto, inspirado en <a href="http://octopress.org/">Octopress</a>.<br/>
                        <span class="copyleft">&copy;</span> pico.dev ${year}
                    </div>
                </div>
            </div>
        </div>
    </footer>
    
    <div id="fb-root"></div>
    <t:ads adsense="adsense"/>
</body>
</html>

Para terminar nos queda ver como sería usar este componente en una página donde queremos usarlo. En la etiqueta html se usa la plantilla con t:type para indicar que esa etiqueta es un componente de Tapestry y se le pasan los aside1 y aside2 que en esta página tienen contenido propio. El contenido de la etiqueta html se sustituirá por la etiqueta de la plantilla, el contenido inlcluido en los componentes aunque esté dentro de la etiqueta html solo se mostrará cuando se haga uso de un , como se hace el componente plantilla. Este es el caso de la página índice de Blog Stack. A pesar de todo el contenido que genera y solo consta de 34 líneas de código, esto muestra lo fácil que es en Tapestry dividir las diferentes partes de una página en componentes que puede ser reutilizados.

 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
<html t:type="layout" t:aside1="aside1" t:aside2="aside2" xmlns:t="http://tapestry.apache.org/schema/tapestry_5_4.xsd" xmlns:p="tapestry:parameter">

<t:data/>

<t:loop source="posts" value="post">
    <t:postcomponent post="post" excerpt="true"/>
</t:loop>

<section class="index-pagination">
    <div class="container-fluid">
        <div class="row">
            <div class="col-xs-4 col-sm-4 col-md-4">
                <t:if test="!lastPage">
                    <a t:type="pagelink" page="index" context="nextContext"><span class="glyphicon glyphicon-arrow-left"></span> Más antiguo</a>
                </t:if>
            </div>    
            <div class="col-xs-4 col-sm-4 col-md-4 col-xs-offset-4 col-sm-offset-4 col-md-offset-4 text-right">
                <t:if test="!firstPage">
                    <a t:type="pagelink" page="index" context="previusContext">Más nuevo <span class="glyphicon glyphicon-arrow-right"></span></a>
                </t:if>
            </div>
        </div>
    </div>
</section>

<t:block id="aside1">
    <t:feeds/>
</t:block>

<t:block id="aside2">
    <t:lastposts/>
    <t:lastsourceswithposts/>
</t:block>
</html>

Usando el mismo componente podemos darle un aspecto común pero variando el contenido de las diferentes secciones. En este caso usamos la misma plantilla donde se muestra la misma cabecera, enlaces de navegación y pie de página pero sin el contenido lateral como en el caso de la página de preguntas frecuentes de Blog Stack, en este caso no usamos los componentes aside.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<html t:type="layout" t:title="Preguntas frecuentes" xmlns:t="http://tapestry.apache.org/schema/tapestry_5_4.xsd" xmlns:p="tapestry:parameter">

<article class="text-justify">
    <header>
        <h1>Preguntas frecuentes</h1>
    </header>

    <h2>¿Qué es Blog Stack?</h2>

    <p>Blog Stack (BS) es una agregador, planeta, o fuente de información de bitácoras sobre programación, desarrollo, desarrollo ágil, software, software libre, hardware,
        gnu/linux o en general temas relacionados con la tecnología.</p>

    <h2>¿Por qué otro agregador?</h2>

    <p>
        Hay varios motivos, la semilla es que quería hacer un proyecto personal con cierta utilidad para otras personas empleando de alguna forma el framework para el desarrollo de
        aplicaciones web <a href="http://tapestry.apache.org/">Apache Tapestry</a>.
    </p>

    ...
</article>

</html>

Por supuesto, podemos crear tantos componentes plantilla como necesitemos en una aplicación y usar uno o otro en función del tipo de página.

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.