
After spending some time on Flex and AIR in my spare time - with Peek in Grails as result - I'm happily coding in Grails again. Ahh what a pleasure ;-)
Some time ago I wanted to create a real-life demo application in Grails. Back in May or June I took the MySQL Sakila sample database and started building a Grails application on top of it. I also integrated Yahoo! User Interface Library for a better and richer user experience like using the DataTable (see also the Grails YUI DataTable example).
As I continued working on the demo application I added a YUI MenuBar and customized the Grails scaffolding templates so it generates a YUI enabled user interface.
Currently I'm only using the YUI DataTable, MenuBar, Button and Grids CSS but I want to extend this to use the YUI Calendar, AutoComplete, Rich Text Editor and Charts and perhaps some other nice-looking YUI widgets as well. Under water it also uses required YUI utilities for easily sending XMLHttpRequests and processing JSON results.
I basically only changed the main.gsp, main.css and scaffolding templates and see below what can be generated.
I also created the Grails on Sakila project hosted on Google Code, so if you want you can browse or download the source code yourself. It is currently under development so you are warned.
Wednesday, August 13, 2008
Grails on Sakila
Monday, June 16, 2008
Grails YUI DataTable example

After I quit looking into the Ext JS library because of the license problems, I wanted to use another AJAX framework which comes bundled with nice widgets. Before I started with Ext I had already looked into The Yahoo! User Interface Library (YUI) but preferred Ext that time. So switching back to YUI makes sense... The only reason I was hesitating was because of Yahoo's possible takeover by Microsoft as it would be uncertain what would happen to YUI on the short and long term. The news that the deal between Microsoft and Yahoo seems to be off the table definitely, made my choise to use YUI easy.
Today I created an example on how to use YUI's DataTable within a Grails application.
The example is based on this simple Country domain class:
class Country {
String country
}
The Grails CountryController looks like:
class CountryController {
def index = { redirect(action: list, params: params) }
def list = {
}
def listData = {
def countryList = Country.list(params)
response.setHeader("Cache-Control", "no-store")
render(contentType: "text/json") {
totalRecords(Country.count())
records {
for (r in countryList) {
country(id: r.id, country: r.country)
}
}
}
}
}
As you can see the list action is doing nothing except rendering the list.gsp. The listData action will be called by the YUI DataTable and returns the records in JSON format together with the total count of records (totalRecords). So at last - and a big chunk of JavaScript code - we need the list.gsp to create and display the YUI DataTable:
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<meta name="layout" content="main" />
<title>Country List</title>
</head>
<body>
<div id="demo">
<div id="paging"></div>
<div id="dt"></div>
</div>
<script type="text/javascript">
// Set up DataSource
var myDataSource = new YAHOO.util.DataSource('${createLink(action: 'listData')}?');
myDataSource.responseType = YAHOO.util.DataSource.TYPE_JSON;
myDataSource.responseSchema = {
resultsList : 'records',
fields : ['id', 'country'],
metaFields : {
totalRecords: 'totalRecords'
}
};
// A custom function to translates sorting and pagination values
// into a query string the server will accept
var buildQueryString = function (state,dt) {
return "offset=" + state.pagination.recordOffset +
"&max=" + state.pagination.rowsPerPage +
"&sort=" + state.sorting.key +
"&order=" + ((state.sorting.dir === YAHOO.widget.DataTable.CLASS_ASC) ? "asc" : "desc");
};
// Custom function to handle pagination requests
var handlePagination = function (state,dt) {
var sortedBy = dt.get('sortedBy');
// Define the new state
var newState = {
startIndex: state.recordOffset,
sorting: {
key: sortedBy.key,
dir: ((sortedBy.dir === YAHOO.widget.DataTable.CLASS_DESC) ? YAHOO.widget.DataTable.CLASS_DESC : YAHOO.widget.DataTable.CLASS_ASC)
},
pagination : { // Pagination values
recordOffset: state.recordOffset, // Default to first page when sorting
rowsPerPage: dt.get("paginator").getRowsPerPage()
}
};
// Create callback object for the request
var oCallback = {
success: dt.onDataReturnSetRows,
failure: dt.onDataReturnSetRows,
scope: dt,
argument: newState // Pass in new state as data payload for callback function to use
};
// Send the request
dt.getDataSource().sendRequest(buildQueryString(newState), oCallback);
};
var myColumnDefs = [
{key: "id", label: "Id", sortable: true},
{key: "country", label: "Country", sortable: true}
];
var myTableConfig = {
initialRequest : 'max=10&offset=0&sort=id&order=asc',
generateRequest : buildQueryString,
paginator : new YAHOO.widget.Paginator({containers: "paging", rowsPerPage: 10}),
paginationEventHandler : handlePagination,
sortedBy : {key: "id", dir: YAHOO.widget.DataTable.CLASS_ASC}
};
var myDataTable = new YAHOO.widget.DataTable('dt', myColumnDefs, myDataSource, myTableConfig);
// Override function for custom server-side sorting
myDataTable.sortColumn = function(oColumn) {
// Default ascending
var sDir = "asc";
// If already sorted, sort in opposite direction
if(oColumn.key === this.get("sortedBy").key) {
sDir = (this.get("sortedBy").dir === YAHOO.widget.DataTable.CLASS_ASC) ? "desc" : "asc";
}
// Define the new state
var newState = {
startIndex: 0,
sorting: { // Sort values
key: oColumn.key,
dir: (sDir === "desc") ? YAHOO.widget.DataTable.CLASS_DESC : YAHOO.widget.DataTable.CLASS_ASC
},
pagination : { // Pagination values
recordOffset: 0, // Default to first page when sorting
rowsPerPage: this.get("paginator").getRowsPerPage()
}
};
// Create callback object for the request
var oCallback = {
success: this.onDataReturnSetRows,
failure: this.onDataReturnSetRows,
scope: this,
argument: newState // Pass in new state as data payload for callback function to use
};
// Send the request
this.getDataSource().sendRequest(buildQueryString(newState), oCallback);
};
</script>
</body>
</html>
The Grails' createLink taglib is used to define the url to retrieve the JSON data from. For the rest, the YUI DataTable configuration and paginator take care of all server side paging and sorting.
Note that I included the following YUI javascript and css files in my main.gsp:
<link rel="stylesheet" type="text/css" href="http://yui.yahooapis.com/2.5.2/build/reset-fonts-grids/reset-fonts-grids.css">
<link rel="stylesheet" type="text/css" href="http://yui.yahooapis.com/2.5.2/build/assets/skins/sam/skin.css">
<script type="text/javascript" src="http://yui.yahooapis.com/2.5.2/build/utilities/utilities.js"></script>
<script type="text/javascript" src="http://yui.yahooapis.com/2.5.2/build/calendar/calendar-min.js"></script>
<script type="text/javascript" src="http://yui.yahooapis.com/2.5.2/build/datasource/datasource-beta-min.js"></script>
<script type="text/javascript" src="http://yui.yahooapis.com/2.5.2/build/datatable/datatable-beta-min.js"></script>
<script type="text/javascript" src="http://yui.yahooapis.com/2.5.2/build/container/container_core-min.js"></script>
<script type="text/javascript" src="http://yui.yahooapis.com/2.5.2/build/menu/menu-min.js"></script>
Off course it's also possible to install the Grails YUI plugin if you don't want to use the hosted files.
PS: a little bit of refactoring to make it more re-usable, and it would not be difficult to use in the scaffolding generation.
Posted by
Marcel Overdijk
at
8:01 AM
8
comments
Friday, February 15, 2008
Grails adaptive AJAX support with the Yahoo! UI Library

Grails ships out of the box with adaptive AJAX support.
By default Grails uses the Prototpe library when invoking AJAX request using the Grails remoteLink, formRemote and submitToRemote AJAX tags.
Not only is Grails providing an easy way of integrating AJAX functionality in your pages, but it also allows you to switch to another AJAX library if needed. This is what we mean with adaptive AJAX support.
One of the use cases to switch to another AJAX library is that this library offers more functionality then the default Prototype library. E.g. you want to use the Yahoo! User Interface Library (YUI) within you application for the autocomplete or calendar functionality. In this case you want the YUI library also to be used for your AJAX tags, so you only depend on YUI and not on both YUI and Prototype.
If you want easy support for YUI in your Grails application then just install the Grails Yahoo! UI Library Plugin which I released today. It downloads and installs automatically the latest YUI 2.4.1 2.5.0 distribution in your application, and registers itself to be used with the adapative AJAX tags. It also contains two helper tags to easily include additional YUI javascript and css files as well.
Note that this plugin does not provide any tags for embedding rich ui components without having to deal with javascript libraries. If you are more interested in such a plugin, have a look at the Grails RichUI Plugin. This plugin contains a set of AJAX components not limited to the YUI library.
For a personal application I want to use the YUI autocomplete and calendar functionality so I'm thinking already of a Grails YUI Widgets Plugin which can be installed on top of the YUI Plugin ...
