(Quick Reference)

5 Layouts with Sitemesh

Version: 6.2.4-SNAPSHOT

Table of Contents

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-app/conf/application.yml
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>