Is there a way in Grails GSP to replace the following
<tmpl:/templates/header />
<!-- tmpl namespace call is equivalent to <g:render template="/templates/header" /> -->
<!-- definition of inner content -->
<tmpl:/templates/footer />
With an outer template? Essentially, a way to import the wrapping outer template,
<outertemplate:templatename>
<!-- header will be rendered from outer template -->
<!-- definition of inner content -->
<!-- footer will be rendered from outer template -->
</outertemplate:templatename>
and the definition of the outer template being something along the lines of
<!-- definition of header content -->
<!-- placeholder or attr for inner content -->
<!-- definition of footer content -->
Encapsulating the wrapping content in a single template versus two. IIRC there was a way to do this under JSF but I can't find an equivalent under GSP.
Ok so what I was looking for was Grails' SiteMesh layout support, which allows me to define commonly used view markup in a more eloquent manner then templates.
So the header and footer content can be placed inside a layout
<html>
<head>
<title><g:layoutTitle/></title>
<g:layoutHead />
</head>
<body>
<!-- HEADER CONTENT DEFINED HERE (or for stuff in head in the head above -->
<g:layoutBody />
<!-- FOOTER CONTENT DEFINED HERE -->
</body>
</html>
And then use the layout,
<html>
<head>
<title>An Example Page</title>
<meta name="layout" content="main" />
</head>
<body>This is my content!</body>
</html>
Which I think is much cleaner then header and footer templates.
You can also nest layouts.
You can create something like this using a tag library.
class SimpleTagLib {
def emoticon = { attrs, body ->
out << body() << (attrs.happy == 'true' ? " :-)" : " :-(")
}
}
This defines a tag emoticon that can be used in gsp like this:
<g:emoticon happy="true">Hi John</g:emoticon>
body() is used to render the tag body content.
(The example is copied from the offical grails documentation)
Related
Lets say I have a gsp page that I want to load all the scripts in a particular folder:
<html>
<head>
{each file in $path}
<asset:javascript src="file" />
</head>
</html>
assuming $path is a directory path. Most templating languages have a way to do this so I'm sure Grails can accomplish it. But I'm not sure how to make it happen. My end goal is this:
ndex
<html>
<head>
<asset:javascript src="js/util.js" />
<asset:javascript src="js/util2.js" />
<asset:javascript src="js/index.js" />
</head>
</html>
Please help.
You can do something like this:
<html>
<head>
<g:each var="file" in="${(new File(path)).listFiles()*.path}">
<asset:javascript src="${file}" />
</g:each>
</head>
</html>
The GSP g:each tag is how iteration is performed in Grails when using GSP. The in attribute is used to specify the Iterable to iterate through. In this case it's the expression:
(new File(path)).listFiles()*.path
The expression means:
new File(path) - Create a Java File object to represent the directory path.
.listFiles() - Returns a list of all files and directories (excluding sub-directories) each represented by File objects.
*.path - A spread operator expression which returns a list of file path Strings, effectively converting the File objects into Strings. It's the equivalent of .collect { it.path }.
In Grails 2.3.7, is there a way to use an expression in a GSP page to add an attribute to a body element? In the code below, the expression in the p element works, but the same expression in the body element causes a error: Expecting '=' after attribute name (${raw('this="that"')}).
<!DOCTYPE html>
<html>
<head>
<title>Test</title>
</head>
<body ${raw('this="that"')}>
<p ${raw('this="that"')}>Hello!</p>
</body>
</html>
I'm trying to do this in a layout and pick up the corresponding attribute from the original page with pageProperty, but the same error occurs on the body element in the page layout as well.
Replacing an attribute value does work in a body element like this:
<body this="${that}">
but this won't work because I do not want the attribute to appear at all if it has no value.
the problem is, that the body tag is replaced by the <g:layoutBody /> tag and therefore could not be set like this.
One solution is to use to set different stuff in the sitemesh layout.
An example of this is shown here:
<html>
<head>
<g:layoutHead/>
</head>
<body class="${pageProperty( name:'body.class' )}">
<g:layoutBody/>
</body>
</html>
may I know what's the difference between <body> and <g:layoutBody>, and how do I use these tags?
body tag is a HTML tag. (nothing to do with grails)
g:layoutBody should be used in templates to allow the concrete views to inject their data into the template.
Official documentation on g:layoutBody is quite helpful.
In particular, this is their example of a decorator layout a.k.a. main.gsp. In this example <g:layoutBody /> will be replaced with the body of the document to be decorated (e.g. index.gsp) and, of course, <g:layoutHead /> will be replaced with the head of the document to be decorated.
<html>
<head>
<script src="global.js" />
<g:layoutHead />
</head>
<body><g:layoutBody /></body>
</html>
Actually I've a master page and I'm adding this master page into another view (Index.cshtml), now I want to add another individual css file into Index.cshtml view.
In the head section of your master page (e.g. _Layout.cshtml) add a RenderSection call. Your header section could look like this:
<head>
<meta charset="utf-8" />
<meta name="description" content="The content description" />
<link rel="shortcut icon" href="#Url.Content("~/Content/images/favicon.ico")" />
<title>#ViewBag.Title</title>
#RenderSection("css", false)
</head>
Then in your Index.cshtml use the section to add your css link:
#section css {
<link href="#Url.Content("~/css/style.css")" rel="stylesheet"/>
}
Note that "css" is a unique name for your section and it needs to match. You can also use this section in any view you want. The part you specify within the css section will then be rendered within the head-tag of your html, just where you put the RenderSection placeholder in your _Layout.cshtml.
Bad practice but you can Just add a html link tag or script tag to the top of the view, modern browsers will still render this. It doesn't always need to be in the head of the master page
You can use Razor sections, or a string collection and store it in ViewBag.CSS, and later render it on the layout, or you can use javascript if you are not profficient with razor
var $ = document; // shortcut
var cssId = 'myCss'; // you could encode the css path itself to generate id..
if (!$.getElementById(cssId))
{
var head = $.getElementsByTagName('head')[0];
var link = $.createElement('link');
link.id = cssId;
link.rel = 'stylesheet';
link.type = 'text/css';
link.href = 'http://website.com/css/stylesheet.css';
link.media = 'all';
head.appendChild(link);
}
I am building a web application using Grails. I decided to use dojo and I added a dojo fisheye menu for begining in the main.gsp so it would be available on all the application's pages.
It works fine for the (home) index.gsp page, but once I select another one, the fisheye menu disapears. If I go back to home it is there. I revised my settings and everything looks ok to me. I am not using anything fancy, just simple things. I am missing something but not able to figure it out.
here is the code in my main.gsp simplified for clarity:
<html>
<head>
...
<g:layoutHead />
<!-- use dojo library ... this has not effect at all -->
<g:javascript library="dojotk"/>
<!-- Load Dojo -->
<script type="text/javascript" src="js/dojotk/dojo/dojo.js"
djConfig="parseOnLoad:true, isDebug:false"></script>
<!-- need fisheye -->
<g:javascript type="text/javascript">
dojo.require("dojox.widget.FisheyeList");
</g:javascript>
<!-- required css for dojo fisheye -->
<style type="text/css">#import "js/dojotk/dojox/widget/FisheyeList/FisheyeList.css";</style>
</head>
<body >
...
<!-- fisheye bar -->
<div id="fisheyebar"><g:render template="/common/fisheyebar"/></div>
<g:layoutBody />
</body>
And here is the _fisheyebar.gsp
<g:javascript>
function load_app(target){
window.location.href=target
}
</g:javascript>
<center >
<div class="outerbar">
<div dojoType="dojox.widget.FisheyeList"
itemWidth="50" itemHeight="50"
itemMaxWidth="200" itemMaxHeight="200"
orientation="horizontal"
effectUnits="2"
itemPadding="10"
attachEdge="top"
labelEdge="bottom"
>
<div dojoType="dojox.widget.FisheyeListItem"
onClick= "load_app('${createLinkTo(dir:'/something')}');"
iconsrc="images/icon_something.png" caption="Web Browser">
</div>
.....
</div>
</div> <!-- outbar -->
</center>
All the pages including the index.gsp have the following:
<head>
<title>some titel</title>
<meta name="layout" content="main" />
</head>
Please not that the usage of template (_fisheyebar) is not the cause, I put the code directly in the main and had the same effect. So what am I missing?
it is in the relative url to dojo's location. it is relative to the root so that's why the index works and not the other pages.
using absolute URLs fixes the problem.
Did you try to move your dojo declaration and imports to your layout template page instead of putting it in your main.gsp ?