Grails g:include can it be done? - grails

Im wondering if it is possible to use g:include to include only the body contents of a given page.
Say i have a main layout page as follows:
<html>
<head>
<title>My start page</title>
<g:layoutHead>
</head>
<body>
<g:layoutBody>
</body>
</html>
Then a main page (index.gsp)
<html>
<head>
<!-- main layout reference -->
<meta name="layout" content="main"/>
</head>
<body>
THIS IS MY INDEX BODY CONTENT WITH AN INCLUDE
<g:include controller="book" action="list"/>
<g:link controller="book" action="list">See the full list!</g:link>
</body>
</html>
And finally the book/list page
<html>
<head>
<!-- main layout reference -->
<meta name="layout" content="main"/>
</head>
<body>
<table>
<g:each in="${books}">
<tr>
<td>${it.author}</td>
<td>${it.title}</td>
<td>${it.price}</td>
</tr>
</g:each>
</table>
</body>
</html>
So what i want to achieve is that the main page (index.gsp) only includes the table defined in the book/list page. However, when i try this it includes the entire html defined (<html> tags and all).
is it possible to do this somehow? i've tried things like <g:include controller="book" action="list" view="someView.gsp"/> however this doesn't work. I really want to avoid having to add a book list logic to the "index controller" i want to reuse my existing controller.
I can't be the first person out there having this usecase, what solutions have you guys come up with?

You can use the applyLayout tag. Simply create an empty.gsp layout with only:
<g:layoutBody/>
And then decorate your include tag with applyLayout:
<g:applyLayout name="empty">
<g:include controller="book" action="list"/>
</g:applyLayout>
See the entry on the Grails guide for further reference.

This is IMHO directly not possible. An idea would be to create a custom tag based on g:include, that strips out parts of the code by e.g. an xpath expression. I'm not aware that this already exists somewhere.
An alternative would be to refactor the body part of book's list.gsp into a template and reference that template from index.gsp using g:render. But this means that the data model has to be available in index.gsp context since g:render does not invoke a controller.
Side note: when using g:include it's a good idea to use the springcache plugin for page fragment caching.

Yes, but you need you need one more level in there. Look at Grails templates. Essentially, you'd have a template: _books.gsp containing:
<table>
<g:each in="${books}">
<tr>
<td>${it.author}</td>
<td>${it.title}</td>
<td>${it.price}</td>
</tr>
</g:each>
</table>
Then your index would be:
<html>
<head>
<!-- main layout reference -->
<meta name="layout" content="main"/>
</head>
<body>
THIS IS MY INDEX BODY CONTENT WITH AN INCLUDE
<g:render template="books">
<g:link controller="book" action="list">See the full list!</g:link>
</body>
</html>
And your list would be:
<html>
<head>
<!-- main layout reference -->
<meta name="layout" content="main"/>
</head>
<body>
<g:render template="books" />
</body>
</html>
(My syntax may not be 100% right, since it's been a couple months since I've done this, but the idea behind templates are short, reusable pieces of GSP code that aren't meant to be displayed on their own.

Related

Problem in sending session to view at Asp.Net MVC

I want to prevent clients to access route Home/Index when they are not logged in. So the code in my view is here:
#{
Layout = null;
if (Session["userId"]==null)
{
Response.Redirect(Url.Action("Index","Login"));
}
}
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>QIndex</title>
</head>
<body>
<div>
<h1>Profile</h1>
<h2>Hello #Session["userName"].ToString() </h2>
<a href=#Url.Action("LogOut","Login")>خروج</a>
</div>
</body>
</html>
Problem is that when i eliminate #Session["userName"].ToString() everything is alright. But when this is not eliminated, an error occurs on this line System.NullReferenceException: 'Object reference not set to an instance of an object.'.
I set a break point on if (Session["userId"]==null). I realized that compiler goes to <h2>Hello #Session["userName"].ToString() </h2> before it checks the if statement and then returns to if.
Just remove .ToString(). It should render the string properly without it.

Can't access index.gsp in grails

I am new to grails and I am trying to access index page. After deleting the default homepage for grails, I getting the following error
404 App1/WEB-INF/grails-app/views/index.jsp
description The requested resource is not available.
I checked UrlMapping and view is set to index
"/"(view:"/index")
"500"(view:'/error')
My controller class
package App1grailsapp
class TeamController {
def index() { }
def teams()
{
[teams:Team.list()]
}
}
And I have two gsp pages,
index
<!--
To change this license header, choose License Headers in Project Properties.
To change this template file, choose Tools | Templates
and open the template in the editor.
-->
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Sample title</title>
</head>
<body>
<h2><g:message code="welcome"/></h2>
<ul>
<li><g:link action="teams">Display all teams</g:link></li>
</ul>
</body>
</html>
and teams
<html>
<head>
<meta name="layout" content="main">
<title>Teams</title>
</head>
<body>
<h2>Teams:</h2>
<table>
<tr>
<thead>
<th>Name</th>
</thead>
</tr>
<g:each var="team" in="${teams}">
<tr>
<td>${team.name}</td>
</tr>
</g:each>
</table>
</body>
</html>
I don't understand why index.gsp page is not being shown?
View resolution for Grails is based on convention. Each controller has a directory under grails-app/views for their associated views. Unless you specify otherwise (via render) the view displayed will match the controller action name.
In your case you need to move your index.gsp to grails-app/views/ and rename your teams.gsp to index.gsp under grails-app/views/team.
Hope this helps. You may want to take a look at the documentation for further information and insight.
Also, you will need to specify what controller is used by the link to show all teams.
<li><g:link controller="team" action="index">Display all teams</g:link></li>

RenderSection Scripts not showing in Head Tag

I have a problem with the following. I have a masterpage which contains the main layout of the site. Then I have a specific view which uses the masterpage as a layout but adds additional scripts like the following:
_Masterpage.cshtml
<html lang="en">
<head>
<meta charset="utf-8" />
<title>#ViewBag.Title - My ASP.NET MVC Application</title>
<link href="~/favicon.ico" rel="shortcut icon" type="image/x-icon" />
<meta name="viewport" content="width=device-width" />
#Styles.Render("~/Content/css")
#Scripts.Render("~/bundles/modernizr")
#RenderSection("head", required: false)
</head>
<body>
<header> ....
_Register.cshtml
#{
Layout = "_Master.cshtml";
}
#section head
{
#Scripts.Render("~/bundles/jqueryval");
#Styles.Render("~/Content/themes/base/css");
}
<div id="body">
<div data-role="page">
#RenderBody()
</div>
</div>
Now my view which contains the form data makes use of the _Register.cshtml, however when the Index in the RegisterController returns the view (like below), the scripts and styles in the _Register.cshtml are not found anywhere in the Head tag.
public ActionResult Index()
{
return View();
}
Can anyone help me please? What am I missing in order to get these scripts/styles in the head tag please?
Thanks
Sections don't work in partial views and that's by design. You may use some custom helpers - Using sections in Editor/Display templates - to achieve similar behavior, but honestly it's the view's responsibility to include the necessary scripts, not the partial's responsibility. I would recommend you using the #scripts section of the main view to do that and not have the partials worry about scripts

Grails display gsp inside div

Hey. Imagine i have two separate gsp pages with diferent css formatting (with name conflicts between two). And i want to "display" or render one page with its ows formatation inside a div in the other page. Imagining this scenario:
page1.gsp
...
...
<div id="here"></div>
...
...
page2.gsp
Hello there!
I want my final page to be:
...
...
Hello there!
...
...
Is it possible to do that?
Yes use the g:render tag and create a "fragment" page to contain your content.
then use the g:render tag to pull it in.
Refer to the documentation or tutorials for more detail on this.
This is very similar to a question I posted a couple of days ago:
Can I display an inner dive with an independent stylesheet?
Is this something you want to work for every page? (Like a layout?)
If that is the case, use SiteMesh (built in already)
{app}/grails-app/views/layouts/mylayout.gsp
<html>
<head>
<g:layoutTitle default="My Application" />
<link rel="stylesheet" href="${resource(dir:'css',file:'layout.css')}" />
<g:layoutHead />
</head>
<body>
<div id="here"><g:layoutBody /></div>
</body>
</html>
{app}/grails-app/views/{somefolder}/page1.gsp
<html>
<head>
<meta name="layout" content="mylayout" />
<link rel="stylesheet" href="${resource(dir:'css',file:'page1.css')}" />
</head>
<body>
Hello There!!!!
</body>
</html>
If you already have that, and are just looking at breaking up you pages and keeping them DRY..
{app}/grails-app/views/{somefolder}/page1.gsp
<html>
<head>
<meta name="layout" content="yourLayout" />
<link rel="stylesheet" href="${resource(dir:'css',file:'page1.css')}" />
</head>
<body>
<div id="here2"><g:render template="page2" model="[foo:'bar']"/></div>
</body>
</html>
* The Model property of render is optional, but works for passing data to the template to be rendered
{app}/grails-app/views/{somefolder}/_page2.gsp
* Notice the "_" before the gsp name. (Convention for Template pages)
Hello There
Checkout the documentation for render and templating

How do I pass a variable into grails template from a Layout file?

So I have a navigation template (/common/_navigation.gsp) that is getting rendered in the Grails projects main Layout file (/layouts/main.gsp). How do I pass some kind of variable/parameter/arg from the individual view files to layout and eventually into the navigation template? I just want the right tab to be highlighted when I'm on a page.
(We've already tried using the Grails Navigation Plugin. Since we have different tabs that point to the same controllers (same view, different filter) it breaks down.)
I do this pattern all the time. In my view, I can attach a property to the page either manually or by using the parameter tag in the view that I'm rendering. Its not documented in the Grails user guide, but its super handy.
<parameter name="foo" value="bar" />
Then I would access the page property by using the pageProperty tag.
<g:set var="activeNavItem" value="${pageProperty(name: 'page.foo')}"/>
The layout doesn't need to handle this variable at all :-)
just use the model parameter
for Example:
view:
<g:render template="../path/template" model="[header:'test', headers:['a','b'], values:[['a':1,'b':2],['a':3,'b':4]] ]"/>
_template:
<br/>
<h3>${header}</h3>
<table>
<thead>
<tr>
<g:each in ="${headers}" var="he">
<th>${he}</th>
</g:each>
</tr>
</thead>
<tbody>
<g:each in ="${values}" var="v">
<tr>
<g:each in ="${headers}" var="h">
<td>${v[h]}</td>
</g:each>
</tr>
</g:each>
</tbody>
</table>
I use an arguably simpler method. Just define a variable at request scope in the individual view before you call your layout. It will be available in all templates used in the request, including the layout and any called via <g:render>
<g:set var="SOMEVARIABLE" value="SOMEVALUE" scope="request"/>
<meta name="layout" content="yourlayout">
Then just reference the variable as you would any other in your layout or other templates pulled in by your view
${SOMEVARIABLE} or <g:if test="${SOMEVARIABLE}">, etc., etc
You'd need to use a page property: http://grails.org/doc/1.1.1/ref/Tags/pageProperty.html
Then pass it into the render tag using the model param.
cheers
Lee
The pattern I like, uses the pageProperty as follows. In the layout, I reference the pageProperty like so:
The layout gsp
<body id="${pageProperty(name: 'page.pageType')}">
<g:render template="/layouts/header" />
<g:layoutBody/>
<g:render template="/layouts/copyright" />
</div>
... and within the <head> section of the specific gsp page (I found that it did NOT work outside the head section), I declare the value like so:
The page gsp
<head>
<meta name="layout" content="main"/>
<parameter name="pageType" value="homePg" />
</head>
Resulting HTML
<body id="homePg">
... header, body and footer ...
</body>
In addition, I can inject a value from the controller model into pageProperty like so:
Controller
def index() {
model: [modelPageType: 'adminPg']
}
The layout gsp (using the same layout as above)
<body id="${pageProperty(name: 'page.pageType')}">
<g:render template="/layouts/header" />
<g:layoutBody/>
<g:render template="/layouts/copyright" />
</div>
The page gsp
<head>
<meta name="layout" content="main"/>
<parameter name="pageType" value="${modelPageType}" />
</head>
Resulting HTML
<body id="adminPg">
... header, body and footer ...
</body>
You can simply reach any variable in any template in the page by getting it from params.
Just type ${params.variable} in layout and you will get your variable.
Based on my readings, I think its not a good practice to pass variables to your layout. Model variables are accessible in your view. I think layouts should be used only to specify the structure of the page while views can be used to fill in the content.

Resources