dependencies {
//...
implementation "org.grails.plugins:gsp:6.2.1"
}
Groovy Server Pages (GSP)
GSP (Groovy Server Pages) - A server-side view rendering technology based on Groovy
Version: 6.2.1
Table of Contents
1 Introduction
Groovy Servers Pages (or GSP for short) is Grails' view technology. It is designed to be familiar for users of technologies such as ASP and JSP, but to be far more flexible and intuitive.
GSP was previously part of Grails core, but since version 3.3 it is an independent Grails plugin that can be used by defining the following dependency in your build.gradle
:
In addition, for production compilation you should apply the grails-gsp
Gradle plugin:
apply plugin: "org.grails.grails-gsp"
GSPs themselves live in the grails-app/views
directory and are typically rendered automatically (by convention) or with the render method such as:
render(view: "index")
A GSP is typically a mix of mark-up and GSP tags which aid in view rendering.
Although it is possible to have Groovy logic embedded in your GSP and doing this will be covered in this document, the practice is strongly discouraged. Mixing mark-up and code is a bad thing and most GSP pages contain no code and needn’t do so. |
A GSP typically has a "model" which is a set of variables that are used for view rendering. The model is passed to the GSP view from a controller. For example consider the following controller action:
def show() {
[book: Book.get(params.id)]
}
This action will look up a Book
instance and create a model that contains a key called book
. This key can then be referenced within the GSP view using the name book
:
${book.title}
Embedding data received from user input has the risk of making your application vulnerable to a Cross Site Scripting (XSS) attack. Please read the documentation on XSS prevention for information on how to prevent XSS attacks. |
2 GSP Basics
In the next view sections we’ll go through the basics of GSP and what is available to you. First off let’s cover some basic syntax that users of JSP and ASP should be familiar with.
GSP supports the usage of <% %>
scriptlet blocks to embed Groovy code (again this is discouraged):
<html>
<body>
<% out << "Hello GSP!" %>
</body>
</html>
You can also use the <%= %>
syntax to output values:
<html>
<body>
<%="Hello GSP!" %>
</body>
</html>
GSP also supports JSP-style server-side comments (which are not rendered in the HTML response) as the following example demonstrates:
<html>
<body>
<%-- This is my comment --%>
<%="Hello GSP!" %>
</body>
</html>
Embedding data received from user input has the risk of making your application vulnerable to an Cross Site Scripting (XSS) attack. Please read the documentation on XSS prevention for information on how to prevent XSS attacks. |
2.1 Variables and Scopes
Within the <% %>
brackets you can declare variables:
<% now = new Date() %>
and then access those variables later in the page:
<%=now%>
Within the scope of a GSP there are a number of pre-defined variables, including:
-
application
- The javax.servlet.ServletContext instance -
applicationContext
The Spring ApplicationContext instance -
flash
- The flash object -
grailsApplication
- The GrailsApplication instance -
out
- The response writer for writing to the output stream -
params
- The params object for retrieving request parameters -
request
- The HttpServletRequest instance -
response
- The HttpServletResponse instance -
session
- The HttpSession instance -
webRequest
- The GrailsWebRequest instance
2.2 Logic and Iteration
Using the <% %>
syntax you can embed loops and so on using this syntax:
<html>
<body>
<% [1,2,3,4].each { num -> %>
<p><%="Hello ${num}!" %></p>
<%}%>
</body>
</html>
As well as logical branching:
<html>
<body>
<% if (params.hello == 'true')%>
<%="Hello!"%>
<% else %>
<%="Goodbye!"%>
</body>
</html>
2.3 Page Directives
GSP also supports a few JSP-style page directives.
The import directive lets you import classes into the page. However, it is rarely needed due to Groovy’s default imports and GSP Tags:
<%@ page import="java.awt.*" %>
GSP also supports the contentType directive:
<%@ page contentType="application/json" %>
The contentType directive allows using GSP to render other formats.
2.4 Expressions
In GSP the <%= %>
syntax introduced earlier is rarely used due to the support for GSP expressions. A GSP expression is similar to a JSP EL expression or a Groovy GString and takes the form ${expr}
:
<html>
<body>
Hello ${params.name}
</body>
</html>
However, unlike JSP EL you can have any Groovy expression within the ${..}
block.
Embedding data received from user input has the risk of making your application vulnerable to a Cross Site Scripting (XSS) attack. Please read the documentation on XSS prevention for information on how to prevent XSS attacks. |
3 GSP Tags
Now that the less attractive JSP heritage has been set aside, the following sections cover GSP’s built-in tags, which are the preferred way to define GSP pages.
The section on Tag Libraries covers how to add your own custom tag libraries. |
All built-in GSP tags start with the prefix g:
. Unlike JSP, you don’t specify any tag library imports. If a tag starts with g:
it is automatically assumed to be a GSP tag. An example GSP tag would look like:
<g:example />
GSP tags can also have a body such as:
<g:example>
Hello world
</g:example>
Expressions can be passed into GSP tag attributes, if an expression is not used it will be assumed to be a String value:
<g:example attr="${new Date()}">
Hello world
</g:example>
Maps can also be passed into GSP tag attributes, which are often used for a named parameter style syntax:
<g:example attr="${new Date()}" attr2="[one:1, two:2, three:3]">
Hello world
</g:example>
Note that within the values of attributes you must use single quotes for Strings:
<g:example attr="${new Date()}" attr2="[one:'one', two:'two']">
Hello world
</g:example>
With the basic syntax out the way, the next sections look at the tags that are built into Grails by default.
3.1 Variables and Scopes
Variables can be defined within a GSP using the set tag:
<g:set var="now" value="${new Date()}" />
Here we assign a variable called now
to the result of a GSP expression (which simply constructs a new java.util.Date
instance). You can also use the body of the <g:set>
tag to define a variable:
<g:set var="myHTML">
Some re-usable code on: ${new Date()}
</g:set>
The assigned value can also be a bean from the applicationContext:
<g:set var="bookService" bean="bookService" />
Variables can also be placed in one of the following scopes:
-
page
- Scoped to the current page (default) -
request
- Scoped to the current request -
flash
- Placed within flash scope and hence available for the next request -
session
- Scoped for the user session -
application
- Application-wide scope.
To specify the scope, use the scope
attribute:
<g:set var="now" value="${new Date()}" scope="request" />
3.2 Logic and Iteration
GSP also supports logical and iterative tags out of the box. For logic there are if, else and elseif tags for use with branching:
<g:if test="${session.role == 'admin'}">
<%-- show administrative functions --%>
</g:if>
<g:else>
<%-- show basic functions --%>
</g:else>
<g:each in="${[1,2,3]}" var="num">
<p>Number ${num}</p>
</g:each>
<g:set var="num" value="${1}" />
<g:while test="${num < 5 }">
<p>Number ${num++}</p>
</g:while>
3.3 Search and Filtering
If you have collections of objects you often need to sort and filter them. Use the findAll and grep tags for these tasks:
Stephen King's Books:
<g:findAll in="${books}" expr="it.author == 'Stephen King'">
<p>Title: ${it.title}</p>
</g:findAll>
The expr
attribute contains a Groovy expression that can be used as a filter. The grep tag does a similar job, for example filtering by class:
<g:grep in="${books}" filter="NonFictionBooks.class">
<p>Title: ${it.title}</p>
</g:grep>
Or using a regular expression:
<g:grep in="${books.title}" filter="~/.*?Groovy.*?/">
<p>Title: ${it}</p>
</g:grep>
The above example is also interesting due to its usage of GPath. GPath is an XPath-like language in Groovy. The books
variable is a collection of Book
instances. Since each Book
has a title
, you can obtain a list of Book titles using the expression books.title
. Groovy will auto-magically iterate the collection, obtain each title, and return a new list!
3.4 Links and Resources
GSP also features tags to help you manage linking to controllers and actions. The link tag lets you specify controller and action name pairing and it will automatically work out the link based on the URL Mappings, even if you change them! For example:
<g:link action="show" id="1">Book 1</g:link>
<g:link action="show" id="${currentBook.id}">${currentBook.name}</g:link>
<g:link controller="book">Book Home</g:link>
<g:link controller="book" action="list">Book List</g:link>
<g:link url="[action: 'list', controller: 'book']">Book List</g:link>
<g:link params="[sort: 'title', order: 'asc', author: currentBook.author]"
action="list">Book List</g:link>
3.5 Forms and Fields
Form Basics
GSP supports many different tags for working with HTML forms and fields, the most basic of which is the form tag. This is a controller/action aware version of the regular HTML form tag. The url
attribute lets you specify which controller and action to map to:
<g:form name="myForm" url="[controller:'book',action:'list']">...</g:form>
In this case we create a form called myForm
that submits to the BookController
's list
action. Beyond that, all the usual HTML attributes apply.
Form Fields
In addition to easy construction of forms, GSP supports custom tags for dealing with different types of fields, including:
-
textField - For input fields of type 'text'
-
passwordField - For input fields of type 'password'
-
checkBox - For input fields of type 'checkbox'
-
radio - For input fields of type 'radio'
-
hiddenField - For input fields of type 'hidden'
-
select - For dealing with HTML select boxes
Each of these allows GSP expressions for the value:
<g:textField name="myField" value="${myValue}" />
GSP also contains extended helper versions of the above tags such as radioGroup (for creating groups of radio tags), localeSelect, currencySelect and timeZoneSelect (for selecting locales, currencies and time zones respectively).
Multiple Submit Buttons
The age-old problem of dealing with multiple submit buttons is also handled elegantly with Grails using the actionSubmit tag. It is just like a regular submit, but lets you specify an alternative action to submit to:
<g:actionSubmit value="Some update label" action="update" />
3.6 Tags as Method Calls
Tags as method calls from GSPs
Tags return their results as a String-like object (a StreamCharBuffer
which has all of the same methods as String) instead of writing directly to the response when called as methods. For example:
Static Resource: ${createLinkTo(dir: "images", file: "logo.jpg")}
This is particularly useful for using a tag within an attribute:
<img src="${createLinkTo(dir: 'images', file: 'logo.jpg')}" />
In view technologies that don’t support this feature you have to nest tags within tags, which becomes messy quickly and often has an adverse effect of WYSIWYG tools that attempt to render the mark-up as it is not well-formed:
<img src="<g:createLinkTo dir='images' file='logo.jpg'/>" />
Tags as method calls from Controllers and Tag Libraries
You can also invoke tags from controllers and tag libraries. Tags within the default g:
namespace can be invoked without the prefix and a StreamCharBuffer
result is returned:
def imageLocation = createLinkTo(dir:"images", file:"logo.jpg").toString()
Prefix the namespace to avoid naming conflicts:
def imageLocation = g.createLinkTo(dir:"images", file:"logo.jpg").toString()
For tags that use a custom namespace, use that prefix for the method call. For example (from the CK Editor plugin):
def editor = ckeditor.editor(name: "text", width: "100%", height: "400")
4 Views and Templates
Grails also has the concept of templates. These are useful for partitioning your views into maintainable chunks, and combined with Layouts provide a highly re-usable mechanism for structured views.
Template Basics
Grails uses the convention of placing an underscore before the name of a view to identify it as a template. For example, you might have a template that renders Books located at grails-app/views/book/_bookTemplate.gsp
:
<div class="book" id="${book?.id}">
<div>Title: ${book?.title}</div>
<div>Author: ${book?.author?.name}</div>
</div>
Use the render tag to render this template from one of the views in grails-app/views/book
:
<g:render template="bookTemplate" model="[book: myBook]" />
Notice how we pass into a model to use using the model
attribute of the render
tag. If you have multiple Book
instances you can also render the template for each Book
using the render tag with a collection
attribute:
<g:render template="bookTemplate" var="book" collection="${bookList}" />
Shared Templates
In the previous example we had a template that was specific to the BookController
and its views at grails-app/views/book
. However, you may want to share templates across your application.
In this case you can place them in the root views directory at grails-app/views or any subdirectory below that location, and then with the template attribute use an absolute location starting with /
instead of a relative location. For example if you had a template called grails-app/views/shared/_mySharedTemplate.gsp
, you would reference it as:
<g:render template="/shared/mySharedTemplate" />
You can also use this technique to reference templates in any directory from any view or controller:
<g:render template="/book/bookTemplate" model="[book: myBook]" />
The Template Namespace
Since templates are used so frequently there is template namespace, called tmpl
, available that makes using templates easier. Consider for example the following usage pattern:
<g:render template="bookTemplate" model="[book:myBook]" />
This can be expressed with the tmpl
namespace as follows:
<tmpl:bookTemplate book="${myBook}" />
Templates in Controllers and Tag Libraries
You can also render templates from controllers using the render controller method. This is useful for JavaScript heavy applications where you generate small HTML or data responses to partially update the current page instead of performing new request:
def bookData() {
def b = Book.get(params.id)
render(template:"bookTemplate", model:[book:b])
}
The render controller method writes directly to the response, which is the most common behaviour. To instead obtain the result of template as a String you can use the render tag:
def bookData() {
def b = Book.get(params.id)
String content = g.render(template:"bookTemplate", model:[book:b])
render content
}
Notice the usage of the g
namespace which tells Grails we want to use the tag as method call instead of the render method.
5 Layouts with Sitemesh
Creating Layouts
Grails leverages Sitemesh, a decorator engine, to support view layouts. Layouts are located in the grails-app/views/layouts
directory. A typical layout can be seen below:
<html>
<head>
<title><g:layoutTitle default="An example decorator" /></title>
<g:layoutHead />
</head>
<body onload="${pageProperty(name:'body.onload')}">
<div class="menu"><!--my common menu goes here--></div>
<div class="body">
<g:layoutBody />
</div>
</body>
</html>
The key elements are the layoutHead, layoutTitle and layoutBody tag invocations:
-
layoutTitle
- outputs the target page’s title -
layoutHead
- outputs the target page’s head tag contents -
layoutBody
- outputs the target page’s body tag contents
The previous example also demonstrates the pageProperty tag which can be used to inspect and return aspects of the target page.
Triggering Layouts
There are a few ways to trigger a layout. The simplest is to add a meta tag to the view:
<html>
<head>
<title>An Example Page</title>
<meta name="layout" content="main" />
</head>
<body>This is my content!</body>
</html>
In this case a layout called grails-app/views/layouts/main.gsp
will be used to lay out the page. If we were to use the layout from the previous section the output would resemble this:
<html>
<head>
<title>An Example Page</title>
</head>
<body onload="">
<div class="menu"><!--my common menu goes here--></div>
<div class="body">
This is my content!
</div>
</body>
</html>
Specifying A Layout In A Controller
Another way to specify a layout is to specify the name of the layout by assigning a value to the "layout" property in a controller. For example, if you have a controller such as:
class BookController {
static layout = 'customer'
def list() { /*...*/ }
}
You can create a layout called grails-app/views/layouts/customer.gsp
which will be applied to all views that the BookController
delegates to. The value of the layout
property may contain a directory structure relative to the grails-app/views/layouts/
directory. For example:
class BookController {
static layout = 'custom/customer'
def list() { /*...*/ }
}
Views rendered from that controller would be decorated with the grails-app/views/layouts/custom/customer.gsp
template.
Layout by Convention
Another way to associate layouts is to use "layout by convention". For example, if you have this controller:
class BookController {
def list() { /*...*/ }
}
You can create a layout called grails-app/views/layouts/book.gsp
, which will be applied to all views that the BookController
delegates to.
Alternatively, you can create a layout called grails-app/views/layouts/book/list.gsp
which will only be applied to the list
action within the BookController
.
If you have both the above-mentioned layouts in place the layout specific to the action will take precedence when the list action is executed.
If a layout is not located using any of those conventions, the convention of last resort is to look for the application default layout which
is grails-app/views/layouts/application.gsp
. The name of the application default layout may be changed by defining the property grails.sitemesh.default.layout
in the application configuration as follows:
grails.sitemesh.default.layout: myLayoutName
With that property in place, the application default layout will be grails-app/views/layouts/myLayoutName.gsp
.
Inline Layouts
Grails' also supports Sitemesh’s concept of inline layouts with the applyLayout tag. This can be used to apply a layout to a template, URL or arbitrary section of content. This lets you even further modularize your view structure by "decorating" your template includes.
Some examples of usage can be seen below:
<g:applyLayout name="myLayout" template="bookTemplate" collection="${books}" />
<g:applyLayout name="myLayout" url="https://www.google.com" />
<g:applyLayout name="myLayout">
The content to apply a layout to
</g:applyLayout>
Server-Side Includes
While the applyLayout tag is useful for applying layouts to external content, if you simply want to include external content in the current page you use the include tag:
<g:include controller="book" action="list" />
You can even combine the include tag and the applyLayout tag for added flexibility:
<g:applyLayout name="myLayout">
<g:include controller="book" action="list" />
</g:applyLayout>
Finally, you can also call the include tag from a controller or tag library as a method:
def content = include(controller:"book", action:"list")
The resulting content will be provided via the return value of the include tag.
5.1 Sitemesh Content Blocks
Although it is useful to decorate an entire page sometimes you may find the need to decorate independent sections of your site. To do this you can use content blocks. To get started, partition the page to be decorated using the <content>
tag:
<content tag="navbar">
... draw the navbar here...
</content>
<content tag="header">
... draw the header here...
</content>
<content tag="footer">
... draw the footer here...
</content>
<content tag="body">
... draw the body here...
</content>
Then within the layout you can reference these components and apply individual layouts to each:
<html>
<body>
<div id="header">
<g:applyLayout name="headerLayout">
<g:pageProperty name="page.header" />
</g:applyLayout>
</div>
<div id="nav">
<g:applyLayout name="navLayout">
<g:pageProperty name="page.navbar" />
</g:applyLayout>
</div>
<div id="body">
<g:applyLayout name="bodyLayout">
<g:pageProperty name="page.body" />
</g:applyLayout>
</div>
<div id="footer">
<g:applyLayout name="footerLayout">
<g:pageProperty name="page.footer" />
</g:applyLayout>
</div>
</body>
</html>
6 Static Resources
Since version 3, Grails integrates with the Asset Pipeline plugin to provide sophisticated static asset management. This plugin is installed by default in new Grails applications.
The basic way to include a link to a static asset in your application is to use the resource tag. This simple approach creates a URI pointing to the file.
However, modern applications with dependencies on multiple JavaScript and CSS libraries and frameworks (as well as dependencies on multiple Grails plugins) require something more powerful.
The issues that the Asset-Pipeline plugin tackles are:
-
Reduced Dependence - The plugin has compression, minification, and cache-digests built in.
-
Easy Debugging - Makes for easy debugging by keeping files separate in development mode.
-
Asset Bundling using require directives.
-
Web application performance tuning is difficult.
-
The need for a standard way to expose static assets in plugins and applications.
-
The need for extensible processing to make languages like LESS or Coffee first class citizens.
The asset-pipeline allows you to define your javascript or css requirements right at the top of the file, and they get compiled on War creation.
Take a look at the documentation for the asset-pipeline to get started.
If you do not want to use the Asset-Pipeline plugin, you can serve the static assets from directories src/main/resources/public
or src/main/webapp
, but the latter one only gets included in WAR packaging but not in JAR packaging.
For example, if you had a file stored at /src/main/resources/public/images/example.png
and your Grails application was running on port 8080, then you could access the file by navigating to http://localhost:8080/static/images/example.jpg.
Cache Configuration for Static Resources
By default, files under src/main/resources/public
or src/main/webapp
are served with an HTTP response header of Cache-Control: no-store
.
To have them be cached by the browser, you can set the configuration setting grails.resources.cachePeriod: number
in application.yml
so that they are served with a response header of Cache-Control: max-age=number
indicating to the browser how many seconds the file should be considered fresh.
7 Making Changes to a Deployed Application
One of the main issues with deploying a Grails application (or typically any servlet-based application) is that any change to the views requires that you redeploy your whole application. If all you want to do is fix a typo on a page, or change an image link, it can seem like a lot of unnecessary work. For such simple requirements, Grails does have a solution: the grails.gsp.view.dir
configuration setting.
How does this work? The first step is to decide where the GSP files should go. Let’s say we want to keep them unpacked in a /var/www/grails/my-app
directory. We add these two properties to the application configuration:
grails.gsp.enable.reload: true
grails.gsp.view.dir: /var/www/grails/my-app/
The first line tells Grails that modified GSP files should be reloaded at runtime. If you don’t have this setting, you can make as many changes as you like, but they won’t be reflected in the running application until you restart. The second line tells Grails where to load the views and layouts from.
The trailing slash on the grails.gsp.view.dir value is important! Without it, Grails will look for views in the parent directory.
|
Setting grails.gsp.view.dir
is optional. If it’s not specified, you can update files directly to the application server’s deployed war directory. Depending on the application server, these files might get overwritten when the server is restarted. Most application servers support "exploded war deployment" which is recommended in this case.
With those settings in place, all you need to do is copy the views from your web application to the external directory. On a Unix-like system, this would look something like this:
mkdir -p /var/www/grails/my-app/grails-app/views cp -R grails-app/views/* /var/www/grails/my-app/grails-app/views
The key point here is that you must retain the view directory structure, including the grails-app/views
bit. So you end up with the path /var/www/grails/my-app/grails-app/views/…
.
One thing to bear in mind with this technique is that every time you modify a GSP, it uses up permgen space. So at some point you will eventually hit "out of permgen space" errors unless you restart the server. So this technique is not recommended for frequent or large changes to the views.
There are also some system properties to control GSP reloading:
Name | Description | Default |
---|---|---|
grails.gsp.enable.reload |
system property for enabling the GSP reload mode (alternative to adding it in the file-based application configuration |
|
grails.gsp.reload.interval |
interval between checking the lastmodified time of the gsp source file, unit is milliseconds |
5000 |
grails.gsp.reload.granularity |
the number of milliseconds leeway to give before deciding a file is out of date. this is needed because different roundings usually cause a 1000ms difference in lastmodified times |
1000 |
GSP reloading is supported for precompiled GSPs since Grails 1.3.5.
8 Tag Libraries
Like Java Server Pages (JSP), GSP supports the concept of custom tag libraries. Unlike JSP, Grails' tag library mechanism is simple, elegant and completely reloadable at runtime.
Quite simply, to create a tag library create a Groovy class that ends with the convention TagLib
and place it within the grails-app/taglib
directory:
class SimpleTagLib {
}
Now to create a tag create a Closure property that takes two arguments: the tag attributes and the body content:
class SimpleTagLib {
def simple = { attrs, body ->
}
}
The attrs
argument is a Map of the attributes of the tag, whilst the body
argument is a Closure that returns the body content when invoked:
class SimpleTagLib {
def emoticon = { attrs, body ->
out << body() << (attrs.happy == 'true' ? " :-)" : " :-(")
}
}
As demonstrated above there is an implicit out
variable that refers to the output Writer
which you can use to append content to the response. Then you can reference the tag inside your GSP; no imports are necessary:
<g:emoticon happy="true">Hi John</g:emoticon>
To help IDEs like Spring Tool Suite (STS) and others autocomplete tag attributes, you should add Javadoc comments to your tag closures with @attr descriptions. Since taglibs use Groovy code it can be difficult to reliably detect all usable attributes.
|
For example:
class SimpleTagLib {
/**
* Renders the body with an emoticon.
*
* @attr happy whether to show a happy emoticon ('true') or
* a sad emoticon ('false')
*/
def emoticon = { attrs, body ->
out << body() << (attrs.happy == 'true' ? " :-)" : " :-(")
}
}
and any mandatory attributes should include the REQUIRED keyword, e.g.
class SimpleTagLib {
/**
* Creates a new password field.
*
* @attr name REQUIRED the field name
* @attr value the field value
*/
def passwordField = { attrs ->
attrs.type = "password"
attrs.tagName = "passwordField"
fieldImpl(out, attrs)
}
}
8.1 Variables and Scopes
Within the scope of a tag library there are a number of pre-defined variables including:
-
actionName
- The currently executing action name -
controllerName
- The currently executing controller name -
flash
- The flash object -
grailsApplication
- The GrailsApplication instance -
out
- The response writer for writing to the output stream -
pageScope
- A reference to the pageScope object used for GSP rendering (i.e. the binding) -
params
- The params object for retrieving request parameters -
pluginContextPath
- The context path to the plugin that contains the tag library -
request
- The HttpServletRequest instance -
response
- The HttpServletResponse instance -
servletContext
- The javax.servlet.ServletContext instance -
session
- The HttpSession instance
8.2 Simple Tags
As demonstrated in the previous example it is easy to write simple tags that have no body and just output content. Another example is a dateFormat
style tag:
def dateFormat = { attrs, body ->
out << new java.text.SimpleDateFormat(attrs.format).format(attrs.date)
}
The above uses Java’s SimpleDateFormat
class to format a date and then write it to the response. The tag can then be used within a GSP as follows:
<g:dateFormat format="dd-MM-yyyy" date="${new Date()}" />
With simple tags sometimes you need to write HTML mark-up to the response. One approach would be to embed the content directly:
def formatBook = { attrs, body ->
out << "<div id=\"${attrs.book.id}\">"
out << "Title : ${attrs.book.title}"
out << "</div>"
}
Although this approach may be tempting it is not very clean. A better approach would be to reuse the render tag:
def formatBook = { attrs, body ->
out << render(template: "bookTemplate", model: [book: attrs.book])
}
And then have a separate GSP template that does the actual rendering.
8.3 Logical Tags
You can also create logical tags where the body of the tag is only output once a set of conditions have been met. An example of this may be a set of security tags:
def isAdmin = { attrs, body ->
def user = attrs.user
if (user && checkUserPrivs(user)) {
out << body()
}
}
The tag above checks if the user is an administrator and only outputs the body content if he/she has the correct set of access privileges:
<g:isAdmin user="${myUser}">
// some restricted content
</g:isAdmin>
8.4 Iterative Tags
Iterative tags are easy too, since you can invoke the body multiple times:
def repeat = { attrs, body ->
attrs.times?.toInteger()?.times { num ->
out << body(num)
}
}
In this example we check for a times
attribute and if it exists convert it to a number, then use Groovy’s times
method to iterate the specified number of times:
<g:repeat times="3">
<p>Repeat this 3 times! Current repeat = ${it}</p>
</g:repeat>
Notice how in this example we use the implicit it
variable to refer to the current number. This works because when we invoked the body we passed in the current value inside the iteration:
out << body(num)
That value is then passed as the default variable it
to the tag. However, if you have nested tags this can lead to conflicts, so you should instead name the variables that the body uses:
def repeat = { attrs, body ->
def var = attrs.var ?: "num"
attrs.times?.toInteger()?.times { num ->
out << body((var):num)
}
}
Here we check if there is a var
attribute and if there is use that as the name to pass into the body invocation on this line:
out << body((var):num)
Note the usage of the parenthesis around the variable name. If you omit these Groovy assumes you are using a String key and not referring to the variable itself. |
Now we can change the usage of the tag as follows:
<g:repeat times="3" var="j">
<p>Repeat this 3 times! Current repeat = ${j}</p>
</g:repeat>
Notice how we use the var
attribute to define the name of the variable j
and then we are able to reference that variable within the body of the tag.
8.5 Tag Namespaces
By default, tags are added to the default Grails namespace and are used with the g:
prefix in GSP pages. However, you can specify a different namespace by adding a static property to your TagLib
class:
class SimpleTagLib {
static namespace = "my"
def example = { attrs ->
//...
}
}
Here we have specified a namespace
of my
and hence the tags in this tag lib must then be referenced from GSP pages like this:
<my:example name="..." />
where the prefix is the same as the value of the static namespace
property. Namespaces are particularly useful for plugins.
Tags within namespaces can be invoked as methods using the namespace as a prefix to the method call:
out << my.example(name:"foo")
This works from GSP, controllers or tag libraries
8.6 Using JSP Tag Libraries
In addition to the simplified tag library mechanism provided by GSP, you can also use JSP tags from GSP.
In order to use JSP support you must ensure you have the grails-web-jsp
dependency on your classpath by adding it to your build.gradle
file:
dependencies {
//...
runtimeOnly "org.grails:grails-web-jsp:6.2.1"
}
Then you will need to declare the JSP taglib to use with the taglib
directive at the top of your GSP file:
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
Besides this you have to configure Grails to scan for the JSP tld files.
This is configured with the grails.gsp.tldScanPattern
setting. It accepts a comma separated String value. Spring’s PathMatchingResourcePatternResolver is used to resolve the patterns.
For example, you could scan for all available tld files by adding this to application.yml
:
grails:
gsp:
tldScanPattern: 'classpath*:/META-INF/*.tld,/WEB-INF/tld/*.tld'
JSTL standard library is no longer added as a dependency by default. In case you are using JSTL, you should also add these dependencies to build.gradle
:
dependencies {
//...
runtimeOnly 'javax.servlet:jstl:1.1.2'
runtimeOnly 'taglibs:standard:1.1.2'
}
Then you can use JSP tags like any other tag:
<fmt:formatNumber value="${10}" pattern=".00"/>
With the added bonus that you can invoke JSP tags like methods:
${fmt.formatNumber(value:10, pattern:".00")}
8.7 Tag return value
A taglib can be used in a GSP as an ordinary tag, or it might be used as a function in other taglibs or GSP expressions.
Internally Grails intercepts calls to taglib closures.
The "out" that is available in a taglib is mapped to a java.io.Writer
implementation that writes to a buffer
that "captures" the output of the taglib call. This buffer is the return value of a tag library call when it’s
used as a function.
If the tag is listed in the library’s static returnObjectForTags
array, then its return value will be written to
the output when it’s used as a normal tag. The return value of the tag lib closure will be returned as-is
if it’s used as a function in GSP expressions or other taglibs.
If the tag is not included in the returnObjectForTags array, then its return value will be discarded. Using "out" to write output in returnObjectForTags is not supported.
Example:
class ObjectReturningTagLib {
static namespace = "cms"
static returnObjectForTags = ['content']
def content = { attrs, body ->
CmsContent.findByCode(attrs.code)?.content
}
}
Given this example cms.content(code: 'something')
call in another taglib or GSP expression would return the value CmsContent.content
directly to the caller without
wrapping the return value in a buffer. It might be worth doing so also because of performance optimization reasons. There is no need to wrap the
tag return value in an output buffer in such cases.