What is the Grails GSP equivalent of ASP's ContentPlaceHolder? - asp.net-mvc

I've been playing around a lot with the templating/layout concepts in Grails GSP. I've ben using layouts/content blocks for imitating ASP's master page behavior.
For example, I am using the tag <g:pageProperty /> in a template to leave a "placeholder" which can be overridden using a <content> tag:
myTemplate.gsp:
<body>
<g:pageProperty name="page.topDiv" />
</body>
myPage.gsp:
<html>
<head>
<meta name="layout" content="myTemplate"></meta>
</head>
<body>
<content tag="topDiv">
My top div
</content>
</body>
</html>
This works great for "appending" content to some spot within a template. However, I really want the behavior I can get in ASP.NET's master pages... which is to provide a "default" rendering of some content, and allow optional overriding. In an ASP.NET Master page, it would look like this:
myMaster.master:
<asp:ContentPlaceHolder id="something" runat="server">
<div>Default text/html here</div>
</asp:ContentPlaceHolder>
someOtherPage.aspx:
<asp:Content contentPlaceHolderId="something" runat="server">
Overriden content here!! I don't need to override this though :)
</asp:Content>
My Question:
Can I do this same default/overriding behavior in Grails' GSP?

There are a few different days you could accomplish this. The g:pageProperty is equivalent to the Sitemesh decorator:getProperty tag, so you can use a default attribute to indicate the default text to use. For example:
<g:pageProperty name="page.topDiv" default="Default text/html here"/>
However, I don't know of a clean way to get HTML content in there. You could use a g:if tag to test for that property and specify default behavior if it doesn't exist:
<g:if test="${pageProperty(name:'page.topDiv')}">
<g:pageProperty name="page.topDiv"/>
</g:if>
<g:else>
<div>Default text/html here</div>
</g:else>
The default content could also live in an external template gsp. The render method could then be used to display that content in the default attribute of g:pageProperty:
<g:pageProperty name="page.topDiv" default="${render(template:'topDiv')}"/>
Where in this case, the default content would be located in _topDiv.gsp.

I think you can try instead.
<g:render template=""><g:render>

Related

In Grails, how do I combine the head tags of a view and a template?

I'm creating a Grails plugin which has a template. The idea is that the developer can just call and get all of the rendering functionality that I build in the template.
The issue I'm facing has to do with the head tags of both the view and the template. Currently, the template in the plugin has a <head> tag with multiple JS files. If the developer of another Grails project decides to use <g:render/>and call my template, the head tag of the view is getting replaced by the head tag of the template.
Is there any way to combine the two? Essentially, if view.gsp has a head tag and my own template (pulled in from another plugin) has a head tag, I want them to be combined.
From this
http://docs.grails.org/3.1.1/guide/theWebLayer.html#layouts
It shows that you can have the head tag of a template and view exists as long as the :
<g:layoutHead />
is in the head of the template to load the head of the view
8.2.4 Layouts with Sitemesh
Creating Layouts
Grails leverages Sitemesh, a decorator engine, to support view layouts. Layouts are located in the grails-app/views/layouts directory. A typical
<html>
<head>
<title><g:layoutTitle default="An example decorator" /></title>
<g:layoutHead />
</head>
<body onload="${pageProperty(name:'body.onload')}">
<div class="menu"><!--my common menu goes here--></div>
<div class="body">
<g:layoutBody />
</div>
</body>
</html>
The key elements are the layoutHead, layoutTitle and layoutBody tag invocations:
layoutTitle - outputs the target page's title
layoutHead - outputs the target page's head tag contents
layoutBody - outputs the target page's body tag contents

Using other layouts inside a Grails GSP layout

I have a Grails 2.4.x app where ~80% of the pages use a simple.gsp layout, and the other pages are all stragglers that don't use any layout/templating at all. But they can't use simple.gsp because its contents don't apply to them.
I have a need to add a header nav to all of these pages (100%) and would like an elegant solution. Ideally, I could create a new layout, say, awesome-header.gsp that contains the header nav. Then:
For any pages (again, ~20%) that don't use the simple.gsp layout, I would just have them use awesome-header.gsp directly; but then...
I would just somehow reference/import/extend simple.gsp to (somehow) use awesome-header.gsp; which now allows the other ~80% pages to use the new header nav
Let's pretend that this is simple.gsp:
<!DOCTYPE html>
<html>
<head>
<title>
<g:layoutTitle default="Some App" />
</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<!-- Lots of stuff -->
<g:layoutHead />
<!-- Lots of stuff -->
</head>
<body>
<!-- Lots of stuff -->
<div id="page-content">
<g:layoutBody />
</div>
<!-- Lots of stuff -->
</body>
</html>
And let's pretend that this is awesome-header.gsp:
<%# page contentType="text/html;charset=UTF-8" %>
<html>
<head>
<title></title>
</head>
<body>
<script id="awesome-header-bootstrap" src="/awesome-header/awesome-header-bootstrap-1.0.js"><script>
<g:layoutBody />
</body>
</html>
Pretty barebones. All I need this awesome-header.gsp layout to do is include a JS right at the top of the <body> element. For the purpose of this question, this JS script is "magic" and fetches the header nav magically for me.
How can reference/import/extend simple.gsp to use awesome-header.gsp?
I don't want the awesome-header.gsp to override any title or header content (either defined inside simple.gsp or in any of the straggler pages)
Any ideas how I could accomplish this setup?
If I well understand, you want a hierarchy between simple.gsp and awesome-header.gsp. So you may look at this link to help you to do that.
An other solution, maybe easier because there isn't a lot of modifications to do, is to use templates:
Put all your HTML / JS code related to your awesome-header inside a template (let say _awesome-header.gsp, the '_' is important !)
Simply put that line inside your 'simple' layout and inside all others pages which are not connected to your 'simple' layout: <g:render template='awesome-header'/>

How can I add meta description and title to my MVC3 pages?

Can someone tell me if there is a recommended way to add Meta information to MVC3 pages. The kind of information that I would like to add is title, description, keywords.
Thanks,
Gemma
I'd use ViewBag for the title and RenderSection for the rest of the head content. This goes in the Master Layout file (_Layout.cshtml):
<head>
<title>#ViewBag.Title</title>
#RenderSection("head", false);
</head>
In your individual views, you will add:
#{
ViewBag.Title = "My Page Title";
}
#section head {
<meta name="description" content="best site ever">
}
EDIT:
Note that the #section head {...} block is optional. You will not get a compilation error if you omit this block. On views where you want metadata you'd supply it.
I'd add it as a seperate view - that way you can call:
#{Html.RenderAction("Head", "Header");}
from within your various layouts and have the same correct header data rendered.
hth
Usually, in master page, we put a ContentPlaceHolder control, call it TitleContent like this:
<head>
<title>
<asp:ContentPlaceHolder ID="TitleContent" runat="server" />
</title>
</head>
And in children pages:
<asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server">
Some Title
</asp:Content>
The same thing can be done for Meta Tags, or you could make a full ContentPlaceHolder for HeadContent and fill it with (title/meta...) in each page individually.
You have to put metatags inside the HEAD tag of HTML. For MVC application it depends there you have HEAD. It could be either page (*.cshmtl) of layout (Layout.cshtml). It is quite better to place into layout, since meta info is shared throught the rest of pages.
// *.cshtml
<!DOCTYPE html>
<html>
<head>
<title>#ViewBag.Title</title>
<meta ... />
</html>
</head>

ASP.NET MVC2 Master Page - Server side script not rendering, first bracket being escaped

I have a master page which I am using as a template to allow me to define meta tags per page. My master page takes in a model which contains the meta information, here is an example of what I am trying to do the following:
<meta name="description" content="<%= Model.description %>" />
<meta name="keywords" content="<%= Model.keywords %>" />
However, when I check the HTML once the page is rendered I get this:
<meta name="description" content="<= Model.description %>" />
<meta name="keywords" content="<= Model.keywords %>" />
If I remove the outer quotation marks from the content e.g. content=<%= Model.description %> it renders the data. It doesn't seem to like the surrounding quotation marks.
Is this a bug with the master pages? If so, what would be the best alternative workaround for this? If not, what am I doing wrong?
I have seen that before and it is a pain. Probably you have a runat="sever" attribute in your head tag like this:
<head runat="server">
if you just made it:
<head>
then you should not see this behavior.
This has always been an issue because it is trying to encode the contents in the attributes. You can get around it by doing this instead:
<%= string.Format("<meta name=\"description\" content=\"{0}\" />", Model.description) %>
<%= string.Format("<meta name=\"keywords\" content=\"{0}\" />", Model.keywords) %>
EDIT: This is not an issue specific to MasterPages. I posted a similar question a long time ago here on SO asking about it and if you read the accepted answer you can see that the framework has specific code for various items in the head tag where it will have a slightly different rendering format and will encode the data.
ASP.NET Webform css link getting mangled

ASP MVC.NET Control css content

I would like to create in my master page a control that depending on the rendered viewpage will change a css file
I was thinking to make a specific controller "CssController" and call this CssController from the src attribute of the css link. This controller will be in charge of choosing the right css and sending it back with text/css header.
question: will this approach break the mvc pattern? Indeed the choose of the design would be in charge of a controller. Is there a better alternative?
What i did in my master page was create a contentplaceholder in the <head> section, as so:
<head runat="server">
<title>Title Here</title>
<script src="<%= Url.Content("~/Scripts/jquery-1.3.2.min.js") %>" type="text/javascript" ></script>
<link href="<%= Url.Content("~/Content/css/site.css") %>" rel="stylesheet" type="text/css" />
<asp:ContentPlaceHolder ID="HeadContent" runat="server" />
</head>
then, when you create a view using the master page, you can add the view-specific css you want into that content place holder. That way, it'll put your site.css first, plus the desired css, javascript, <script> blocks, whatever, on that particular view.
If you are using separate css files for separate themes, then those would be in fact the view and controller would work. So it does not seem to violate the pattern. But, it does seem to be a more complicated solution than others.
The common way I have seen to do this is with a Jquery Style Switcher
http://www.cssnewbie.com/simple-jquery-stylesheet-switcher/
With a little bit of code you can determine the default style to show on each page
The basic MVC example sets Page Title via the controller. I'm not sure this is any different. If you were using it (for instance) to have users be able to select different skins for their experience, I'm not sure there is any other way to do it, regardless of whether or not it violates the pattern.

Resources