Last updated by
3 years ago
Page: Using ExtJS Layouts with Grails Layouts, Version:30
Using ExtJS Layouts with Grails Layouts
Synopsis
If you're like me, using CSS to manage page layout is a nightmare. One of the many features of the Ext JS library is the ability to programically define page layouts with javascript, no CSS skills required.It's likely that in your Grails app you will want to define some kind of template that defines your master layout (grails-app/views/layouts/main.gsp) which has a section that other pages fill in. This tutorial will show you one way of achieving this while using Ext for your layout.This is not an Ext tutorial
I will not be explaining the hows and whys of Ext here. Check the Ext documentation as it is packed with lots of good examples.Ext layouts
Ext lets you lay the page out in terms of regions. Here is some Ext code to define a layout that has two nested layouts…contentInnerLayout = new Ext.BorderLayout('content', { center: {} });headerInnerLayout = new Ext.BorderLayout('header', { center: {margins: {top: 10, left: 5, right: 10, bottom: 10}}, west: {initialSize: 200, margins: {top: 10, left: 10, right: 5, bottom: 10}} });layout = new Ext.BorderLayout('container', { north: {initialSize: 50}, center: {} });layout.beginUpdate(); headerInnerLayout.add('center', new Ext.ContentPanel('banner', {fitToFrame: true})); headerInnerLayout.add('west', new Ext.ContentPanel('logo', {fitToFrame: true})); contentInnerLayout.add('center', new Ext.ContentPanel('include', {fitToFrame: true})); layout.add('north', new Ext.NestedLayoutPanel(headerInnerLayout, {fitToFrame: true})); layout.add('center', new Ext.NestedLayoutPanel(contentInnerLayout)); layout.endUpdate();
Tutorial
Goal
Building on the above code, we want all our pages to have a layout like the picture above, but any page can (optionally) introduce other nested layouts in the include section.Installing Ext
First of all, you will need to add Ext to your Grails app. I downloaded the Ext 1.1 beta 1 and dropped the whole directory into web-app/js for simplicity.Our main.gsp file
Our grails-app/views/layouts/main.gsp becomes …<html> <head> <title><g:layoutTitle default="Grails" /></title> <link rel="stylesheet" href="${createLinkTo(dir:'js',file:'ext-1.1-beta1/resources/css/ext-all.css')}"></link> <link rel="stylesheet" href="${createLinkTo(dir:'js',file:'ext-1.1-beta1/resources/css/ytheme-aero.css')}"></link> <script type="text/javascript" src="${createLinkTo(dir:'js',file:'ext-1.1-beta1/adapter/ext/ext-base.js')}"></script> <script type="text/javascript" src="${createLinkTo(dir:'js',file:'ext-1.1-beta1/ext-all-debug.js')}"></script> <script type="text/javascript" charset="utf-8"> Ext.BLANK_IMAGE_URL = "${createLinkTo(dir:'js',file:'ext-1.1-beta1/resources/images/default/s.gif')}"; </script> <g:javascript library="application" /> <g:layoutHead /> </head> <body> <div id="container"> <div id="header"> <div id="logo" style="background-color: red;">logo</div> <div id="banner" style="background-color: blue;">banner</div> </div> <div id="content"> <div id="include"> <g:layoutBody /> </div> </div> </div> </body> </html>
<g:javascript library="application" />Creating an Application class
To make things easier I have created a simple {{Application}} js class that defines my master Ext layout and provides a hook for other pages to modify the layout.This is my web-app/js/application.js file ..code: nullApplication = function(){ this.layoutDecorator = null; this.init = function () { // Our master layout layout = new Ext.BorderLayout('container', { north: {initialSize: 50}, center: {} }); // Our standard header headerInnerLayout = new Ext.BorderLayout('header', { center: {margins: {top: 10, left: 5, right: 10, bottom: 10}}, west: {initialSize: 200, margins: {top: 10, left: 10, right: 5, bottom: 10}} }); // Included content contentInnerLayout = new Ext.BorderLayout('content', { center: {} }); layout.beginUpdate(); headerInnerLayout.add('center', new Ext.ContentPanel('banner', {fitToFrame: true})); headerInnerLayout.add('west', new Ext.ContentPanel('logo', {fitToFrame: true})); contentInnerLayout.add('center', this.getContentInnerPanel()); layout.add('north', new Ext.NestedLayoutPanel(headerInnerLayout, {fitToFrame: true})); layout.add('center', new Ext.NestedLayoutPanel(contentInnerLayout)); layout.endUpdate(); } this.getContentInnerPanel = function() { if (this.layoutDecorator) { var includeLayout = new Ext.BorderLayout('include'); this.layoutDecorator(includeLayout); return new Ext.NestedLayoutPanel(includeLayout, {fitToFrame:true}); } else { return new Ext.ContentPanel('include', {fitToFrame:true}); } } }var app = new Application(); Ext.EventManager.onDocumentReady(app.init, app, true);code: nullLets have a look at that init function piece by piece …code: null// Our master layout layout = new Ext.BorderLayout('container', { north: {initialSize: 50}, center: {} });code: nullHere we are splitting the page into a top section and a bottom sectioncode: nullheaderInnerLayout = new Ext.BorderLayout('header', { center: {margins: {top: 10, left: 5, right: 10, bottom: 10}}, west: {initialSize: 200, margins: {top: 10, left: 10, right: 5, bottom: 10}} });code: nullThis is a layout we will use for the header section (north part of the master layout). We are splitting it into a 200px wide left section and a section on the right that takes up the rest of the real estate. We have also defined some margins around each section.code: nullcontentInnerLayout = new Ext.BorderLayout('content', { center: {} });code: nullThis will be our include layout. Just one central section.code: nulllayout.beginUpdate(); headerInnerLayout.add('center', new Ext.ContentPanel('banner', {fitToFrame: true})); headerInnerLayout.add('west', new Ext.ContentPanel('logo', {fitToFrame: true})); contentInnerLayout.add('center', this.getContentInnerPanel()); layout.add('north', new Ext.NestedLayoutPanel(headerInnerLayout, {fitToFrame: true})); layout.add('center', new Ext.NestedLayoutPanel(contentInnerLayout)); layout.endUpdate();code: nullHere we are mapping the content (divs) to the different regions of the layout. Notice how you can either map a {{Ext.ContentPanel}} to a region of a layout or a {{Ext.NestedLayoutPanel}}.The {{getContentInnerPanel}} function
This function is responsible for returning either a panel of some kind to be rendered into the include section of the layout.code: nullthis.getContentInnerPanel = function() { if (this.layoutDecorator) { var includeLayout = new Ext.BorderLayout('include'); this.layoutDecorator(includeLayout); return new Ext.NestedLayoutPanel(includeLayout, {fitToFrame:true}); } else { return new Ext.ContentPanel('include', {fitToFrame:true}); } }code: nullOur Application class defines a public field called {{layoutDecorator}} which is expected to be a function. If it is not null, it is passed an instance of {{Ext.BorderLayout}} to augment. We will see an example of this later.If no layout decorator has been supplied, we simply return a panel of the include section.The global {{app}} object
Note that we are creating a global instance of {{Application}} and calling it {{app}}var app = new Application();An example page
Here is an example on how to insert a nested layout in the include section ....Not much to that at all. Here is what it looks like ...<html> <head> <meta name="layout" content="main" /> <title>Decorated</title> <script type="text/javascript" charset="utf-8"> app.layoutDecorator = function(layout) { layout.addRegion('west', {initialSize: 355, margins: {top: 10, left: 10, right: 5, bottom: 10}}); layout.add('west', new Ext.ContentPanel('west-div', {fitToFrame:true})); layout.addRegion('center', {margins: {top: 10, left: 5, right: 10, bottom: 10}}); layout.add('center', new Ext.ContentPanel('centre-div', {fitToFrame:true})); }; </script> </head> <body> <div id="west-div" style="background-color: orange;"></div> <div id="centre-div" style="background-color: green;"></div> </body> </html>Setting a {{layoutDecorator}} is completely optional. The following is also a valid page …
Which would look like this ...<html> <head> <meta name="layout" content="main" /> <title>Non Decorated</title> </head> <body> Grails + Ext is cool! </body> </html>Summary
This is just an example of one way that you could utilise Ext layouts and Grails layouts. Hopefully this example has given you some ideas on how you could utilise the power of Ext layouts within your Grails project.
Setting a {{layoutDecorator}} is completely optional. The following is also a valid page …
