Tuesday 2 December 2008

grails: Templates


Previously I've used Tiles to provide a common look and feel to web applications.

Template frameworks like Tiles achieve this effect by enabling developers to define page fragments which are then assembled into a complete page at run time. This approach is quite appealing since the fragments are by nature reusable view components, thereby enhancing maintainability and reducing development time.

Grails uses 'SiteMesh' to perform this function, and does so in a simple and flexible way. No XML configuration here !!!

A typical page layout is displayed in the figure on the left. This is made up from the following view components. The topbar will display time/date and a welcome message identifying the user, and potentially a 'logout' button. The header will showcase the name of the application, and the navbar will contain a menu for navigation. The central panel will contain the content to be displayed to the user. Finally the footer will hold information such as Terms & Conditions, a Copyright notice etc.

To implement the layout shown here execute the following steps,

Step 1: start
You need a project, ideally populated with at least a single domain class with it's associated controllers and views. The picture below shows the default layout generated by grails.



Step 2: create re-usable templates
With the exception of the header and content fragments, we start by creating the templates for the remaining view elements. Create a folder called 'common' under the views/ folder. In this folder create the following files
  • _topbar.gsp
  • _navbar.gsp
  • _footer.gsp
Note that these files should start with an underscore, '_' and should not contain any HTML tags such as <head>, <body>.

Step 3: populate _topbar.gsp

<div id="welcome">
12th June 2008
</div>

<div id="menu">
Welcome Ned Flanders | <a href="#">logout</a>
</div>




Step 4: populate _navbar.gsp

<div class='navbarBox'>

<div class="navcontainer1">
<h1 class="panelHeader">
Users
</h1>
<ul class="navlist">
<li>
<g:link controller="user" action="list">
List Users
</g:link>
</li>
<li>
<g:link controller="user" action="create">
Add User
</g:link>
</li>
<li>
<g:link controller="user" action="edit">
Edit User
</g:link>
</li>
<li style="color:grey"><a href="#">Remove User</a></li>
</ul>
</div>

</div>

<div class="navbarBox">

<div class="navcontainer1">
<h1 class="panelHeader">
Actions
</h1>
<ul class="navlist">
<li><a href="#">do something 1</a></li>
<li><a href="#">do something 2</a></li>
<li><a href="#">do something 3</a></li>
<li><a href="#">do something 4</a></li>
<li><a href="#">do something 5</a></li>
</ul>
</div>

</div>

<div class='navbarBox'>

<div class="navcontainer1">
<h1 class="panelHeader">
Leisure
</h1>
<ul class="navlist">
<li><a href="#">Activity 1</a></li>
<li><a href="#">Activity 2</a></li>
<li><a href="#">Activity 3</a></li>
<li><a href="#">Activity 4</a></li>
<li><a href="#">Activity 5</a></li>
</ul>
</div>

</div>




Step 5: populate _footer.gsp

<span class="copyright"> © 2008</span>
<span class="tandc"> <a href="#">Terms & Conditions </a></span>



Step 6: modify main.gsp

Once the reusable view components have been defined, we include them into the layout. Open the file /views/layout/main.gsp and replace all the code between the <body> tags with the following lines of code

<div id="page">

<div id="topbar">
<g:render template="/common/topbar" />
</div>

<div id='header'>
<h1 style="font-size:62px;text-align:center;"> Moongrails </h1>
</div>

<div id="content">
<g:layoutBody />
</div>

<div id='navbar'>
<g:render template="/common/navbar" />
</div>

<div id="footer">
<g:render template="/common/footer" />
</div>

</div>



The content in the central fragment will be populated by the gsp pages in the various view folders. When the user navigates to any particular page, e.g. /user/list.gsp, grails imports in the contents of that page and inserts it into the content div via the <g:layoutBody /> tag.

Step 7: modify main.css

The final step is to code up the styling for the various templates.

/* --------------------------------------*/
/* PAGE container */
/* --------------------------------------*/
/* Contains page contents e.g header,navbar, content, footer */
#page {
width: 90%;
margin: 0px auto;
padding: 4px 0;
text-align:left;
}


/* --------------------------------------*/
/* TOPBAR */
/* --------------------------------------*/
#topbar {
text-align:left;
padding:5px 0;
border-top: 1px solid #333;
background: #aaaaaa;
height: 15px;
}

#topbar #welcome{
float:left;
text-align:left;
font-size: 12px;
padding: 0px 0px 0px 10px;
color: #ffffff;
}

#topbar #menu{
float: right;
text-align: right;
font-size: 12px;
color: #ffffff;
}

#topbar #menu a{
color: #ffffff;
padding: 0px 10px 0px 0px;
}

/* --------------------------------------*/
/* HEADER */
/* --------------------------------------*/
#header {
background: #227ce8;
height: 150px;
margin: 0px auto;
border-top: solid 2px #000000;
}

#header h1 {
font-family:Arial,sans-serif;
color: white;
padding: 20px 0 0 6px;
font-size:1.6em;
}

/* --------------------------------------*/
/* CONTENT */
/* --------------------------------------*/
#content {
float: left;
width: 80%;
color: #000;
margin: 0 0.5em 0 0;
padding: 0px 10px 5px 0px;
}

/* --------------------------------------*/
/* NAVBAR */
/* --------------------------------------*/
#navbar {
float: right;
width: 210px;
color: #ff0000;
padding: 0px 5px 0px 5px;
margin: 0 0 0 0;
border-left: 1px solid #d3d3d3;
border-right: 1px solid #d3d3d3;
}


/*2.12.08 IE correction to drop sidbarBox*/
html>body #navbar .navbarBox {

width: 200px;
color: #0000ff;
background: #2175bc;
margin: 0px 5px 5px 5px ; /*ensure there's a gap between text in navbar and start of navbar*/
}

/*Header for boxes in navbar menu*/
#navbar .navbarBox h1{
background: #227ce8;
color: #ffffff;
font-weight: normal;
font-size: 16px;
}

/* --------------------------------------*/
/* FOOTER */
/* --------------------------------------*/
#footer {
border: solid 1px #000000;
height: 20px;
clear:both;
padding:5px;
margin: 5px 5px 5px 5px;
background: #ababab url(../images/footer.png) repeat;
}

#footer .tandc{
float: right;
color: #0000ff;
}

#footer .copyright{
float: left;
color: #0000ff;
}

/*----------------------------------------------------------*/
/* Improved vertical menu for navbar: see http://css.maxdesign.com.au/listamatic/vertical10.htm
/*----------------------------------------------------------*/

h1.panelHeader{
font-weight: bold;
text-align: center;
}

/*need html>body hack otw menu boxes, e.g. navbarbx all merge together*/
html>body .navcontainer1
{
width: 100%;
border-right: 1px solid #000;
font-family: Verdana, Lucida, Geneva, Helvetica, Arial, sans-serif;
background-color: #227ee8;
color: #333;
}

.navcontainer1 ul
{
list-style: none;
margin: 0;
padding: 0;
border-top: 2px solid #000000;
}

.navcontainer1 li
{
border-bottom: 1px solid #90bade;
margin: 0;
}

.navcontainer1 li a
{
display: block;
padding: 5px 5px 5px 0.5em;
border-left: 10px solid #1958b7;
border-right: 10px solid #508fc4;
background-color: #2175bc;
color: #ffffff;
text-decoration: none;
}

html>body .navcontainer1 li a { width: auto; }

.navcontainer1 li a:hover
{
border-left: 10px solid #1c64d1;
border-right: 10px solid #5ba3e0;
background-color: #2586d7;
color: #fff;
}





The menu in the navbar was styled using resources from Listomatic's Menu Tutorial. This is an excellent site which provides hints on how to create and style vertical/horizontal css menus.

The final result is displayed below.



The application suffers from at least a couple of problems when viewed using IE6. One of these shows the menu items in the navbar displayed in gray even though the css marks them to be white. To resolve this simply remove 'a:visited' from the line

a:link, a:visited, a:hover {



in the file main.css.

This example was developed using grails 1.03 and can be downloaded [templates.zip] from here.

3 comments:

RH said...

Thank you so much for providing this information. I have used Django before and am currently evaluating Grails to see if it can have an effective template system.

This example is a great start. I was wondering if it was possible to access the navbar template and programatically add/remove elements.

Please do let me know your thoughts. Again, thank you for posting this demo!!!!

geezenslaw said...

Hey! great demo. I downloaded the zip and ran the grails upgrade. Just one curiosity: everything runs and dislays as expected except the content portion. Originally, the content frame was the default Grails way of doing things. Nothing I change in the main.css has any affect upon the content frame. All other aspects of the webpage seem to render according to main.css except the content. pls advise, David.e

Oodles Technologies said...

hello,finally i solved my problem with the help of your blog it,very nice and unique content...FFMPEG Development