7.2 Groovy Server Pages - Reference Documentation
Authors: Graeme Rocher, Peter Ledbrook, Marc Palmer, Jeff Brown, Luke Daley, Burt Beckwith, Lari Hotari
Version: 2.3.8
Table of Contents
7.2 Groovy Server Pages
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.GSPs live in thegrails-app/views
directory and are typically rendered automatically (by convention) or with the render method such as:render(view: "index")
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)] }
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 an Cross Site Scripting (XSS) attack. Please read the documentation on XSS prevention for information on how to prevent XSS attacks.
7.2.1 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>
<%= %>
syntax to output values:<html> <body> <%="Hello GSP!" %> </body> </html>
<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.
7.2.1.1 Variables and Scopes
Within the<% %>
brackets you can declare variables:<% now = new Date() %>
<%=now%>
application
- The javax.servlet.ServletContext instanceapplicationContext
The Spring ApplicationContext instanceflash
- The flash objectgrailsApplication
- The GrailsApplication instanceout
- The response writer for writing to the output streamparams
- The params object for retrieving request parametersrequest
- The HttpServletRequest instanceresponse
- The HttpServletResponse instancesession
- The HttpSession instancewebRequest
- The GrailsWebRequest instance
7.2.1.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>
<html> <body> <% if (params.hello == 'true')%> <%="Hello!"%> <% else %> <%="Goodbye!"%> </body> </html>
7.2.1.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.*" %>
<%@ page contentType="application/json" %>
7.2.1.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>
${..}
block.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.
7.2.2 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 />
<g:example> Hello world </g:example>
<g:example attr="${new Date()}"> Hello world </g:example>
<g:example attr="${new Date()}" attr2="[one:1, two:2, three:3]"> Hello world </g:example>
<g:example attr="${new Date()}" attr2="[one:'one', two:'two']"> Hello world </g:example>
7.2.2.1 Variables and Scopes
Variables can be defined within a GSP using the set tag:<g:set var="now" value="${new Date()}" />
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>
<g:set var="bookService" bean="bookService" />
page
- Scoped to the current page (default)request
- Scoped to the current requestflash
- Placed within flash scope and hence available for the next requestsession
- Scoped for the user sessionapplication
- Application-wide scope.
scope
attribute:<g:set var="now" value="${new Date()}" scope="request" />
7.2.2.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>
7.2.2.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>
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>
<g:grep in="${books.title}" filter="~/.*?Groovy.*?/"> <p>Title: ${it}</p> </g:grep>
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!
7.2.2.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>
7.2.2.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. Theurl
attribute lets you specify which controller and action to map to:<g:form name="myForm" url="[controller:'book',action:'list']">...</g:form>
myForm
that submits to the BookController
's list
action. Beyond that all of 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
<g:textField name="myField" value="${myValue}" />
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" />
7.2.2.6 Tags as Method Calls
One major different between GSP tags and other tagging technologies is that GSP tags can be called as either regular tags or as method calls from controllers, tag libraries or GSP views.Tags as method calls from GSPs
Tags return their results as a String-like object (aStreamCharBuffer
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")}
<img src="${createLinkTo(dir: 'images', file: 'logo.jpg')}" />
<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 defaultg:
namespace can be invoked without the prefix and a StreamCharBuffer
result is returned:def imageLocation = createLinkTo(dir:"images", file:"logo.jpg").toString()
def imageLocation = g.createLinkTo(dir:"images", file:"logo.jpg").toString()
def editor = fckeditor.editor(name: "text", width: "100%", height: "400")
7.2.3 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 atgrails-app/views/book/_bookTemplate.gsp
:<div class="book" id="${book?.id}"> <div>Title: ${book?.title}</div> <div>Author: ${book?.author?.name}</div> </div>
grails-app/views/book
:<g:render template="bookTemplate" model="[book: myBook]" />
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 theBookController
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" />
<g:render template="/book/bookTemplate" model="[book: myBook]" />
The Template Namespace
Since templates are used so frequently there is template namespace, calledtmpl
, available that makes using templates easier. Consider for example the following usage pattern:<g:render template="bookTemplate" model="[book:myBook]" />
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 Ajax 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])
}
def bookData() { def b = Book.get(params.id) String content = g.render(template:"bookTemplate", model:[book:b]) render content }
g
namespace which tells Grails we want to use the tag as method call instead of the render method.
7.2.4 Layouts with Sitemesh
Creating Layouts
Grails leverages Sitemesh, a decorator engine, to support view layouts. Layouts are located in thegrails-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--></menu> <div class="body"> <g:layoutBody /> </div> </div> </body> </html>
layoutTitle
- outputs the target page's titlelayoutHead
- outputs the target page's head tag contentslayoutBody
- outputs the target page's body tag contents
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>
grails-app/views/layouts/main.gsp
will be used to layout 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() { … }
}
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() { … }
}
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() { … } }
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 may not be 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 a property
in grails-app/conf/Config.groovy
as follows:grails.sitemesh.default.layout = 'myLayoutName'
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="http://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" />
<g:applyLayout name="myLayout"> <g:include controller="book" action="list" /> </g:applyLayout>
def content = include(controller:"book", action:"list")
7.2.5 Static Resources
Grails 2.0 integrates with the Resources plugin to provide sophisticated static resource management. This plugin is installed by default in new Grails applications.The basic way to include a link to a static resource 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 Resources framework tackles are:- Web application performance tuning is difficult
- Correct ordering of resources, and deferred inclusion of JavaScript
- Resources that depend on others that must be loaded first
- The need for a standard way to expose static resources in plugins and applications
- The need for an extensible processing chain to optimize resources
- Preventing multiple inclusion of the same resource
7.2.5.1 Including resources using the resource tags
Pulling in resources with r:require
To use resources, your GSP page must indicate which resource modules it requires. For example with the jQuery plugin, which exposes a "jquery" resource module, to use jQuery in any page on your site you simply add:<html> <head> <r:require module="jquery"/> <r:layoutResources/> </head> <body> … <r:layoutResources/> </body> </html>
r:require
multiple times in a GSP page, and you use the "modules" attribute to provide a list of modules:<html> <head> <r:require modules="jquery, main, blueprint, charting"/> <r:layoutResources/> </head> <body> … <r:layoutResources/> </body> </html>
Rendering the links to resources with r:layoutResources
When you have declared the resource modules that your GSP page requires, the framework needs to render the links to those resources at the correct time.To achieve this correctly, you must include the r:layoutResources tag twice in your page, or more commonly, in your GSP layout:<html> <head> <g:layoutTitle/> <r:layoutResources/> </head> <body> <g:layoutBody/> <r:layoutResources/> </body> </html>
Adding page-specific JavaScript code with r:script
Grails has the javascript tag which is adapted to defer to Resources plugin if installed, but it is recommended that you callr:script
directly when you need to include fragments of JavaScript code.This lets you write some "inline" JavaScript which is actually not rendered inline, but either in the <head> or at the end of the body, based on the disposition.Given a Sitemesh layout like this:<html> <head> <g:layoutTitle/> <r:layoutResources/> </head> <body> <g:layoutBody/> <r:layoutResources/> </body> </html>
<html> <head> <title>Testing r:script magic!</title> </head> <body> <r:script disposition="head"> window.alert('This is at the end of <head>'); </r:script> <r:script disposition="defer"> window.alert('This is at the end of the body, and the page has loaded.'); </r:script> </body> </html>
Linking to images with r:img
This tag is used to render<img>
markup, using the Resources framework to process the resource on the fly (if configured to do so - e.g. make it eternally cacheable).This includes any extra attributes on the <img>
tag if the resource has been previously declared in a module.With this mechanism you can specify the width, height and any other attributes in the resource declaration in the module, and they will be pulled in as necessary.Example:<html> <head> <title>Testing r:img</title> </head> <body> <r:img uri="/images/logo.png"/> </body> </html>
g:img
tag as a shortcut for rendering <img>
tags that refer to a static resource. The Grails img tag is Resources-aware and will delegate to r:img
if found. However it is recommended that you use r:img
directly if using the Resources plugin.Alongside the regular Grails resource tag attributes, this also supports the "uri" attribute for increased brevity.See r:resource documentation for full details.
7.2.5.2 Other resource tags
r:resource
This is equivalent to the Grails resource tag, returning a link to the processed static resource. Grails' owng:resource
tag delegates to this implementation if found, but if your code requires the Resources plugin, you should use r:resource
directly.Alongside the regular Grails resource tag attributes, this also supports the "uri" attribute for increased brevity.See r:resource documentation for full details.r:external
This is a resource-aware version of Grails external tag which renders the HTML markup necessary to include an external file resource such as CSS, JS or a favicon.See r:resource documentation for full details.7.2.5.3 Declaring resources
A DSL is provided for declaring resources and modules. This can go either in yourConfig.groovy
in the case of application-specific resources, or more commonly in a resources artefact in grails-app/conf
.Note that you do not need to declare all your static resources, especially images. However you must to establish dependencies or other resources-specific attributes. Any resource that is not declared is called "ad-hoc" and will still be processed using defaults for that resource type.Consider this example resource configuration file, grails-app/conf/MyAppResources.groovy
:modules = { core { dependsOn 'jquery, utils' resource url: '/js/core.js', disposition: 'head' resource url: '/js/ui.js' resource url: '/css/main.css', resource url: '/css/branding.css' resource url: '/css/print.css', attrs: [media: 'print'] } utils { dependsOn 'jquery' resource url: '/js/utils.js' } forms { dependsOn 'core,utils' resource url: '/css/forms.css' resource url: '/js/forms.js' } }
bundle:'someOtherName'
on each resource, or call defaultBundle
on the module (see resources plugin documentation).It declares dependencies between them using dependsOn
, which controls the load order of the resources.When you include an <r:require module="forms"/>
in your GSP, it will pull in all the resources from 'core' and 'utils' as well as 'jquery', all in the correct order.You'll also notice the disposition:'head'
on the core.js
file. This tells Resources that while it can defer all the other JS files to the end of the body, this one must go into the <head>
.The CSS file for print styling adds custom attributes using the attrs
map option, and these are passed through to the r:external
tag when the engine renders the link to the resource, so you can customize the HTML attributes of the generated link.There is no limit to the number of modules or xxxResources.groovy artefacts you can provide, and plugins can supply them to expose modules to applications, which is exactly how the jQuery plugin works.To define modules like this in your application's Config.groovy, you simply assign the DSL closure to the grails.resources.modules
Config variable.For full details of the resource DSL please see the resources plugin documentation.
7.2.5.4 Overriding plugin resources
Because a resource module can define the bundle groupings and other attributes of resources, you may find that the settings provided are not correct for your application.For example, you may wish to bundle jQuery and some other libraries all together in one file. There is a load-time and caching trade-off here, but often it is the case that you'd like to override some of these settings.To do this, the DSL supports an "overrides" clause, within which you can change thedefaultBundle
setting for a module, or attributes of individual resources that have been declared with a unique id:modules = { core { dependsOn 'jquery, utils' defaultBundle 'monolith' resource url: '/js/core.js', disposition: 'head' resource url: '/js/ui.js' resource url: '/css/main.css', resource url: '/css/branding.css' resource url: '/css/print.css', attrs: [media: 'print'] } utils { dependsOn 'jquery' defaultBundle 'monolith' resource url: '/js/utils.js' } forms { dependsOn 'core,utils' defaultBundle 'monolith' resource url: '/css/forms.css' resource url: '/js/forms.js' } overrides { jquery { defaultBundle 'monolith' } } }
7.2.5.5 Optimizing your resources
The Resources framework uses "mappers" to mutate the resources into the final format served to the user.The resource mappers are applied to each static resource once, in a specific order. You can create your own resource mappers, and several plugins provide some already for zipping, caching and minifying.Out of the box, the Resources plugin provides bundling of resources into fewer files, which is achieved with a few mappers that also perform CSS re-writing to handle when your CSS files are moved into a bundle.Bundling multiple resources into fewer files
The 'bundle' mapper operates by default on any resource with a "bundle" defined - or inherited from adefaultBundle
clause on the module. Modules have an implicit default bundle name the same as the name of the module.Files of the same kind will be aggregated into this bundle file. Bundles operate across module boundaries:modules = { core { dependsOn 'jquery, utils' defaultBundle 'common' resource url: '/js/core.js', disposition: 'head' resource url: '/js/ui.js', bundle: 'ui' resource url: '/css/main.css', bundle: 'theme' resource url: '/css/branding.css' resource url: '/css/print.css', attrs: [media: 'print'] } utils { dependsOn 'jquery' resource url: '/js/utils.js', bundle: 'common' } forms { dependsOn 'core,utils' resource url: '/css/forms.css', bundle: 'ui' resource url: '/js/forms.js', bundle: 'ui' } }
Making resources cache "eternally" in the client browser
Caching resources "eternally" in the client is only viable if the resource has a unique name that changes whenever the contents change, and requires caching headers to be set on the response.The cached-resources plugin provides a mapper that achieves this by hashing your files and renaming them based on this hash. It also sets the caching headers on every response for those resources. To use, simply install the cached-resources plugin.Note that the caching headers can only be set if your resources are being served by your application. If you have another server serving the static content from your app (e.g. Apache HTTPD), configure it to send caching headers. Alternatively you can configure it to request and proxy the resources from your container.Zipping resources
Returning gzipped resources is another way to reduce page load times and reduce bandwidth.The zipped-resources plugin provides a mapper that automatically compresses your content, excluding by default already compressed formats such as gif, jpeg and png.Simply install the zipped-resources plugin and it works.Minifying
There are a number of CSS and JavaScript minifiers available to obfuscate and reduce the size of your code. At the time of writing none are publicly released but releases are imminent.7.2.5.6 Debugging
When your resources are being moved around, renamed and otherwise mutated, it can be hard to debug client-side issues. Modern browsers, especially Safari, Chrome and Firefox have excellent tools that let you view all the resources requested by a page, including the headers and other information about them.There are several debugging features built in to the Resources framework.X-Grails-Resources-Original-Src Header
Every resource served in development mode will have the X-Grails-Resources-Original-Src: header added, indicating the original source file(s) that make up the response.Adding the debug flag
If you add a query parameter _debugResources=y to your URL and request the page, Resources will bypass any processing so that you can see your original source files.This also adds a unique timestamp to all your resource URLs, to defeat any caching that browsers may use. This means that you should always see your very latest code when you reload the page.Turning on debug all the time
You can turn on the aforementioned debug mechanism without requiring a query parameter, but turning it on in Config.groovy:grails.resources.debug = true
7.2.5.7 Preventing processing of resources
Sometimes you do not want a resource to be processed in a particular way, or even at all. Occasionally you may also want to disable all resource mapping.Preventing the application of a specific mapper to an individual resource
All resource declarations support a convention of noXXXX:true where XXXX is a mapper name.So for example to prevent the "hashandcache" mapper from being applied to a resource (which renames and moves it, potentially breaking relative links written in JavaScript code), you would do this:modules = { forms { resource url: '/css/forms.css', nohashandcache: true resource url: '/js/forms.js', nohashandcache: true } }
Excluding/including paths and file types from specific mappers
Mappers have includes/excludes Ant patterns to control whether they apply to a given resource. Mappers set sensible defaults for these based on their activity, for example the zipped-resources plugin's "zip" mapper is set to exclude images by default.You can configure this in yourConfig.groovy
using the mapper name e.g:// We wouldn't link to .exe files using Resources but for the sake of example: grails.resources.zip.excludes = ['**/*.zip', '**/*.exe']// Perhaps for some reason we want to prevent bundling on "less" CSS files: grails.resources.bundle.excludes = ['**/*.less']
Controlling what is treated as an "ad-hoc" (legacy) resource
Ad-hoc resources are those undeclared, but linked to directly in your application without using the Grails or Resources linking tags (resource, img or external).These may occur with some legacy plugins or code with hardcoded paths in.There is a Config.groovy setting grails.resources.adhoc.patterns which defines a list of Servlet API compliant filter URI mappings, which the Resources filter will use to detect such "ad-hoc resource" requests.By default this is set to:grails.resources.adhoc.patterns = ['images/*', '*.js', '*.css']
7.2.5.8 Other Resources-aware plugins
At the time of writing, the following plugins include support for the Resources framework:7.2.6 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>
<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>
7.2.7 Making Changes to a Deployed Application
One of the main issues with deploying a Grails application (or typically any servlet-based one) 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: thegrails.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 lines to grails-app/conf/Config.groovy
:
grails.gsp.enable.reload = true grails.gsp.view.dir = "/var/www/grails/my-app/"
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
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 | altervative system property for enabling the GSP reload mode without changing Config.groovy | |
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 |
7.2.8 GSP Debugging
Viewing the generated source code
- Adding "?showSource=true" or "&showSource=true" to the url shows the generated Groovy source code for the view instead of rendering it. It won't show the source code of included templates. This only works in development mode
- The saving of all generated source code can be activated by setting the property "grails.views.gsp.keepgenerateddir" (in Config.groovy) . It must point to a directory that exists and is writable.
- During "grails war" gsp pre-compilation, the generated source code is stored in grails.project.work.dir/gspcompile (usually in ~/.grails/(grails_version)/projects/(project name)/gspcompile).
Debugging GSP code with a debugger
Viewing information about templates used to render a single url
GSP templates are reused in large web applications by using theg:render
taglib. Several small templates can be used to render a single page.
It might be hard to find out what GSP template actually renders the html seen in the result.
The debug templates -feature adds html comments to the output. The comments contain debug information about gsp templates used to render the page.Usage is simple: append "?debugTemplates" or "&debugTemplates" to the url and view the source of the result in your browser.
"debugTemplates" is restricted to development mode. It won't work in production.Here is an example of comments added by debugTemplates :
<!-- GSP #2 START template: /home/.../views/_carousel.gsp
precompiled: false lastmodified: … -->
.
.
.
<!-- GSP #2 END template: /home/.../views/_carousel.gsp
rendering time: 115 ms -->