Receta Grails: Marcar una pestaña como actual

Es bastante habitual en el desarrollo web que, teniendo un sistema de pestañas, se requiera marcar la que corresponde a la actual. Esto habitualmente se realiza con listas y CSS, luego comprobando programáticamente qué se está ejecutando y aplicar el estilo al elemento correspondiente.


<ul>
<li class="act"> <a href="...">News</a></li>
<li><a href="...">Forum</a></li>
...
...
</ul>

Tanto con Grails como con cualquier framework podemos acceder a la petición en la vista, comprobar los parámetros para ver qué elemento de la lista es el actual, y en cada elemento de la lista comprobar si se la acción está relacionada con dicho elemento para asignarle el class. En fin, nuestra vista quedaría algo así:


<ul>
<li ${(params.controller=='news' &&(!params.action || params.action=='index'))?'class="act"':''}><g.link controller="news">News</g.link></li>
<li ${(params.controller=='forum' && params.action=='last')?'class="act"':''}><g.link controller="forum" action="last">Forum</g.link></li>
...
...
</ul>

Pero gracias al sistema de taglibs de grails, lo podemos hacer de forma mucho más limpia y más DRY.

Creamos un taglib, por ejemplo TabsTagLib:

grails create-tag-lib tabs

Ya se nos habrá creado en nuestrapp/grails-app/taglib/TabsTagLib.groovy, donde deberemos añadir una closure que actuará como nuestro tag para esto, el código quedará algo así:


class TabsTagLib {
 def actualTabWithLink={ attrs, body ->
  def selectedClass=(attrs.controller==controllerName && (attrs.action?attrs.action==actionName:true))?'class="act"':''
  out << '<li '+selectedClass+'>'+ g.link(attrs){body()}+'</li>'
 }
}

Y nuestra visa pasaría a quedar así:

<ul>
<actualTabWithLink controller="news"> News </actualTabWithLink>
<actualTabWithLink controller="forum" action="last"> Forum </actualTabWithLink>
...
...
</ul>

La explicación rápida del taglib(para más información la documentación de taglibs de Grails):

(attrs, body ->) attrs son atributos del tag(controller, action...) y body el cuerpo dentro del tag(en nuestro caso News o Forum).

Comparamos controllerName y actionName con los atributos pasados al taglib, attrs.controller siempre y el attrs.action éste último sólo si tiene valor. Si el resultado es que es el actual asignamos a selectedClass class="act", si no es así una cadena vacía.

Como desde un taglib podemos acceder a otros taglibs, llamamos al taglib g.link de forma parecida a la vista, en este caso le pasamos todos los atributos y el cuerpo que vengan de la llamada a actualTabWithLink g.link(attrs){body()}, que nos devolverá la cadena del link.

Por último, sólo debemos concatenar la cadena del link a los tags li con el class="act" en el controller/action actual y hacemos el append a la variable implícita out, que no es otra cosa que el Writer de salida.

En fin, que esta es una forma simple de aprovechar los taglibs de Grails para tener un código mucho más legible en las vistas y más DRY. Veáse también que de ésta forma no debemos preocuparnos de los cambios de mapping de URLs ni de si un acción es la de por defecto en un controller, que entonces no es necesario especificarla en la URL para que se ejecute. También hay que tener en cuenta que para éste caso sólo controlamos los controllers y actions, nos encontraremos en casos dónde podríamos necesitar controlar algún otro parámetro.