Sunday, March 30, 2008

Holy Grail of 3 Column Layout

While Grails really makes the MVC portion of web development easier, Cascading Style Sheets (CSS) is still really tough. I wanted a 3 Column layout with a header and footer created using CSS. This sounds easy enough, but it was more difficult than you would think. Especially if you want it to work with multiple browsers.

I spent several hours working on it, more than I would like to admit. Then I ran across Matthew James Talyor's work on 3 column liquid layouts. Matthew did a really nice job and it works exactly as you would expect. So I tried to weave it into a site I was creating. I was using Firefox and everything was going great until I tried to bring it up in Explorer. Ooops!

To make a long store short, I went back to the beginning and scrapped all my pre-existing CSS and started from Matthews. That did the trick. It rendered correctly in Firefox and Explorer both.



I was so happy figured I would probably use this layout again. So, I created a generic Grails application preconfigured to use the 3 column layout. As you would expect, all of the real work is in main.gsp and a new css file I created css file holygrail.css.

So in the spirit of hoping to make someone else's life a little easier, here are the main.gsp and holygrail.css files.

main.gsp


<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html>
<head>
<title><g:layoutTitle default="www.Grails.org"/></title>
<link rel="stylesheet" href="${createLinkTo(dir: 'css',
file: 'holygrail.css')}"/>
<meta http-equiv="Content-Type" content="application/xhtml+xml; charset=utf-8"/>
<meta name="description" content="The Holy Grail 3 column Liquid Layout. Pixel widths. Cross-Browser. Equal Height Columns."/>
<meta name="keywords" content="The Holy Grail 3 column Liquid Layout. Pixel widths. Cross-Browser. Equal Height Columns."/>
<meta name="robots" content="index, follow"/>
<link rel="shortcut icon" href="${createLinkTo(dir: 'images', file: 'favicon.ico')}" type="image/x-icon"/>
<g:layoutHead/>
</head>
<body>
<!--
This is a Grails example of the Holy Grail 3 column Liquid Layout work by Mathew Taylor.
For more information, please see his original work at:

http://matthewjamestaylor.com/blog/ultimate-3-column-holy-grail-pixels.htm
-->

<div id="header">
<!-- Header start -->
<g:render template="/common/header"/>
<!-- Header end -->
</div>
<div class="colmask holygrail">
<div class="colmid">
<div class="colleft">
<div class="col1wrap">
<div class="col1">
<!-- Column 1 start -->
<g:layoutBody/>
<!-- Column 1 end -->
</div>
</div>
<div class="col2">
<!-- Column 2 start -->
<g:render template="/common/leftbar"/>
<!-- Column 2 end -->
</div>
<div class="col3">
<!-- Column 3 start -->
<g:render template="/common/rightbar"/>
<!-- Column 3 end -->
</div>
</div>
</div>
</div>
<div id="footer">
<!-- Footer start -->
<g:render template="/common/footer"/>
<!-- Footer end -->
</div>

</body>
</html>



holygrail.css



/* General styles */
body {
margin:0;
padding:0;
border:0; /* This removes the border around the viewport in old versions of IE */
width:100%;
background:#fff;
min-width:600px; /* Minimum width of layout - remove line if not required */
/* The min-width property does not work in old versions of Internet Explorer */
font-size:90%;
}
a {
color:#369;
}
a:hover {
color:#fff;
background:#369;
text-decoration:none;
}
h1, h2, h3 {
margin:.8em 0 .2em 0;
padding:0;
}
p {
margin:.4em 0 .8em 0;
padding:0;
}
img {
margin:10px 0 5px;
}
/* Header styles */
#header {
clear:both;
float:left;
width:100%;
}
#header {
border-bottom:1px solid #000;
}
#header p,
#header h1,
#header h2 {
padding:.4em 15px 0 15px;
margin:0;
}
#header ul {
clear:left;
float:left;
width:100%;
list-style:none;
margin:10px 0 0 0;
padding:0;
}
#header ul li {
display:inline;
list-style:none;
margin:0;
padding:0;
}
#header ul li a {
display:block;
float:left;
margin:0 0 0 1px;
padding:3px 10px;
text-align:center;
background:#eee;
color:#000;
text-decoration:none;
position:relative;
left:15px;
line-height:1.3em;
}
#header ul li a:hover {
background:#369;
color:#fff;
}
#header ul li a.active,
#header ul li a.active:hover {
color:#fff;
background:#000;
font-weight:bold;
}
#header ul li a span {
display:block;
}
/* 'widths' sub menu */
#layoutdims {
clear:both;
background:#eee;
border-top:4px solid #000;
margin:0;
padding:6px 15px !important;
text-align:right;
}
/* column container */
.colmask {
position:relative; /* This fixes the IE7 overflow hidden bug and stops the layout jumping out of place */
clear:both;
float:left;
width:100%; /* width of whole page */
overflow:hidden; /* This chops off any overhanging divs */
}
/* holy grail 3 column settings */
.holygrail {
background:#ff9; /* Right column background colour */
}
.holygrail .colmid {
float:left;
width:200%;
margin-left:-200px; /* Width of right column */
position:relative;
right:100%;
background:#fff; /* Centre column background colour */
}
.holygrail .colleft {
float:left;
width:100%;
margin-left:-50%;
position:relative;
left:400px; /* Left column width + right column width */
background:#FFD8B7; /* Left column background colour */
}
.holygrail .col1wrap {
float:left;
width:50%;
position:relative;
right:200px; /* Width of left column */
padding-bottom:1em; /* Centre column bottom padding. Leave it out if it's zero */
}
.holygrail .col1 {
margin:0 215px; /* Centre column side padding:
Left padding = left column width + centre column left padding width
Right padding = right column width + centre column right padding width */
position:relative;
left:200%;
overflow:hidden;
}
.holygrail .col2 {
float:left;
float:right; /* This overrides the float:left above */
width:170px; /* Width of left column content (left column width minus left and right padding) */
position:relative;
right:15px; /* Width of the left-had side padding on the left column */
}
.holygrail .col3 {
float:left;
float:right; /* This overrides the float:left above */
width:170px; /* Width of right column content (right column width minus left and right padding) */
margin-right:45px; /* Width of right column right-hand padding + left column left and right padding */
position:relative;
left:50%;
}
/* Footer styles */
#footer {
clear:both;
float:left;
width:100%;
text-align: center;
border-top:1px solid #000;
}
#footer p {
padding:10px;
margin:0;
}




Friday, March 21, 2008

Grails Filters, Form Submission workaround - Revisited

Good News! Grails 1.0.2 resolves the Filters, Form Submission problem.

I downloaded Grails 1.0.2 and retested with the application below. The workaround is no longer needed. Filters work as expected when submitting an edit, update, or delete request from a form.

Big Thanks to the Grails Team.

Wednesday, March 19, 2008

Grails Filters, Form Submission workaround

Grails Filters are very cool. Much better than action interceptors in my opinion. So much better that I will be surprised if I write another action interceptors.

There is just one small hiccup in the current version of Grails (1.0.1).

Lets say you create a User object and have your standard CRUD actions on it. What if you want to make sure that the only person allowed to update the user record is the actual user. You would neet to put guard logic on the UserController's Edit, Update, and Delete actions. You could do that or create a filter.

Give it a try using the filter below.
class UserFilters {
def filters =
userModificationCheck(controller:'user', action:'*') {
before = {
def currActionName = (actionName == null ? 'index' : actionName)
println "=================> BEFORE: The actionName is now ${currActionName}"
}
after = {
def currActionName = (actionName == null ? 'index' : actionName)
println "=================> AFTER: The actionName is now ${currActionName}"
}
}
}
}


This is a simple filter that prints the action name. After all you only want to do this for edit, update, and delete actions. So you want to make sure you can get the action names.

What you will find is that you use use a fully pathed URL such as http://localhost:8080/myApp/user/edit/1, everything works fine. But if you go to the User Listing > Select a User > and press the form's Edit button, . . . Ooops the action in the before filter doesn't say edit, is says index.

Probably not what you expected, I know I didn't. There is a bug in Grails 1.01, it is scheduled to be fixed in 1.0.2.

Luckly, there is a simple work around for this problem. In the filter, try printing out the params. You will discover a _action_**** entry in the params. Grails uses the entry to determine which action to call on the controller.

Try replacing the currActionName line with the following:


def currActionName = actionName ?: params.find {it.key ==~ "_action_.*"}?.value?.toLowerCase()


It uses a regular expression to find a key beginning with _action_ and graps the value.

This little work around should get you going again.

Monday, March 3, 2008

Grails Podcast is BACK!

The Grails Podcast is BACK!

Glen Smith (Groovy Blogs, Gravel), has joined Seven as a cohost on the Grails Podcast.

In the latest podcast, Seven and Glen bring us upto speed on the latest Groovy and Grails, and the G2X conference.

Saturday, March 1, 2008

Groovy Closures and Java Methods

Steven Devijver has posted a couple of VERY good articles on Groovy DZone about Groovy Closures and Java Methods. I highly recommend these articles.

Higher Order functions with Groovy, part 1, part 2, part 3

They do a good job of explaining all of the ins and outs of closures and methods in Groovy.