Loading...

Wednesday, April 13, 2011

Grails Goodness: Applying Layouts in Layouts Revisited

In the blog post Applying Layouts in Layouts we learned how to reuse layouts in a Grails application. Peter Ledbrook added a comment suggesting to use the <g:applyLayout/> in the Groovy Server Pages in the layouts directory instead of in the views. So this way we can use the default method of applying layouts in our views (using for example the <meta/> tag) and keep all <g:applyLayout/> tags in the layout pages. In this post we see how we can support this for our applications and still reuse the layouts like before.

Please read the previous blog post to see the layout structure of our sample Grails application. The main layout page is still grails-app/views/layouts/page.gsp and doesn't need to be changed:

<%-- File: grails-app/views/layouts/page.gsp --%>
<!DOCTYPE html>
<html>
<head>
    <title><g:layoutTitle default="Grails"/></title>
    <link rel="stylesheet" href="${resource(dir: 'css', file: 'main.css')}"/>
    <link rel="stylesheet" href="${resource(dir: 'css', file: 'layout.css')}"/>
    <link rel="stylesheet" href="${resource(dir: 'css', file: 'fonts.css')}"/>
    <link rel="shortcut icon" href="${resource(dir: 'images', file: 'favicon.ico')}" type="image/x-icon"/>
    <g:layoutHead/>
</head>
<body>
<div id="header" class="clearfix">
    <div id="logo">
        <g:link uri="/"><g:message code="nav.home"/></g:link>
        <p><g:message code="title.website"/></p>
    </div>

    <div id="searchform">
        <g:form controller="search">
            <fieldset class="search">
                <input type="text" class="search-input" value="${message(code:'search.box.search')}" name="search" id="search-phrase" maxlength="100"/>
                <input type="submit" value="${message(code: 'search.box.submit')}" />
            </fieldset>
        </g:form>
    </div>

    <div id="navmenu">
        <ul>
            <li><g:link uri="/"><g:message code="nav.home"/></g:link></li>
            <li><g:link controller="product" action="list"><g:message code="nav.products"/></g:link></li>
        </ul>
    </div>
</div>

<g:layoutBody/>

<div id="footer">
    <p>Copyright © 2011 Hubert A. Klein Ikkink - <a href="http://www.mrhaki.com">mrhaki</a></p>
</div>
</body>
</html>

The layout for the homepage is defined by fiveblocks.gsp in the layouts directory. We change this file and use <g:applyLayout/> to apply the main page layout instead of using <meta name="layout" content="page"/>:

<%-- File: grails-app/views/layouts/fiveblocks.gsp --%>
<g:applyLayout name="page">
    <html>
    <head>
        <title><g:layoutTitle/></title>
        <g:layoutHead/>
    </head>
    <body>
    <div id="banner">
        <g:pageProperty name="page.banner"/>
    </div>

    <div id="left">
        <g:pageProperty name="page.left1"/>
        <g:pageProperty name="page.left2"/>
        <g:pageProperty name="page.left3"/>

        <div id="box-left">
            <g:pageProperty name="page.box-left"/>
        </div>

        <div id="box-right">
            <g:pageProperty name="page.box-right"/>
        </div>
    </div>

    <div id="right">
        <g:pageProperty name="page.right1"/>
        <g:pageProperty name="page.right2"/>
        <g:pageProperty name="page.right3"/>
    </div>
    </body>
    </html>
</g:applyLayout>

And now we use <meta name="layout" content="fiveblocks"/> in the homepage Groovy server page and define the content for our Sitemesh content blocks.

<%-- File: grails-app/views/templates/homepage.gsp --%>
<html>
<head>
    <meta name="layout" content="fiveblocks"/>
    <title><g:message code="title.homepage"/></title>
</head>
<body>
<content tag="banner">
    <h1>Welcome to Grails Layout Demo</h1>
</content>

<content tag="left1">
    <p>...</p>
</content>

<content tag="box-left">
    <p>...</p>
</content>

<content tag="box-right">
    <p>...</p>
</content>

<content tag="right1"> 
    <p>...</p> 
</content> 
</body> 
</html> 

We notice the changes aren't big compared to the previous solution, but using <meta name="layout" content="..."/> in the views is more compliant to what we already learned when using layouts in views.

Also keep in mind Peter Ledbrook's warning about having layouts nested more than one deep:

Finally, if you do have layouts nested more than one deep (not necessarily a good idea, but that's what grails.org is currently doing) then it's worth noting that every layout must have the <g:layout*/> and <g:pageProperty/> tags, otherwise the contents don't propagate properly.

Sources of the sample Grails application can be found at GitHub.

4 comments:

PfisterA said...

Hi , can you add a small webflow to your example ? thanxs Andreas

mrhaki said...

@PfisterA: You can read the previous post to see an overview of the pages that make up the website.

Carsten Block said...

Hi!

I tried to do the following:
1.) rename page.gsp -> base.gsp
2.) In fiveblocks.gsp:
Adjust g:applyLayout name="page" --> g:applyLayout name="base"
Adjust g:pageProperty name="page.left1" --> g:pageProperty name="base.left1" etc.

However, the page rendered empty space where pageProperties were supposed to be rendered. I had to prefix all g:pageProperty calls with "page" instead of "base" to make this work again. Not quite sure why this is the case though.

Android app development said...

Thanks for the post. I liked it. Keep going I follow you.
Android app developer| Android apps development|

Post a Comment