I have some GSP variables set in the "main" layout file of my Grails application (included below). The values of these variables don't appear to be accessible in the GSP pages that are rendered by the sitemesh. Also, they are not visible in any templates that are rendered by the tag. I have tried setting the scope="request" (see code) below, but that doesn't appear to make any difference. I'm clearly not understanding the scoping rules for GSP variables.
Can anyone clarify how GSP variables are scoped and make a recommendation on how I can communicate them from the layout all the way down to templates (if I can at all).
<!DOCTYPE html>
<%-- <html lang="${org.springframework.web.servlet.support.RequestContextUtils.getLocale(request).toString().replace('_', '-')}"> --%>
<html lang="${session.'org.springframework.web.servlet.i18n.SessionLocaleResolver.LOCALE'}">
<head>
<title><g:layoutTitle default="${meta(name:'app.name')}" /></title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="description" content="">
<meta name="author" content="">
<asset:javascript src="bootstrap.js" />
<theme:load />
<asset:javascript src="application.js" />
<asset:stylesheet src="application.css" />
<asset:link rel="shortcut icon" href="favicon.ico" type="image/x-icon" />
<g:layoutHead />
<%-- Defineform body layout column dimensions. These values are used by Bootstrap based forms to
layout using configured column widths and offsets. --%>
<g:set var="labelWidth" value="${grailsApplication.config.ark.layout.labelWidth ?: 'col-sm-3'}" scope="request" />
<g:set var="controlWidth" value="${grailsApplication.config.ark.layout.controlWidth ?: 'col-sm-4'}" scope="request" />
<g:set var="controlOffset" value="${grailsApplication.config.ark.layout.controlWidth ?: 'col-sm-offset-3'}" scope="request" />
<%-- For Javascript see end of body --%>
</head>
<body>
<g:render plugin="arkUi" template="/layouts/menu/navbar"/>
<g:render plugin="arkUi" template="/layouts/content"/>
<g:render plugin="arkUi" template="/layouts/footer"/>
<!-- Include deferred Javascript files and other resources -->
<asset:deferredScripts/>
</body>
</html>
The issue here is that Grails first parses the target GSP page to (among other things) determine which layout to use, then parses the layout GSP and combines them. So the layout can see variables you set in the page but not vice-versa.
Related
I can't make my open graph tags accessible to Facebook. When I try to share a page, the site links properly, but the og attributes don't show up, and the title just displays as "422". I'm not sure what this means.
When I run the facebook debugger https://developers.facebook.com/tools/debug/og/object/ I get the following error, which isn't very helpful:
Error parsing input URL, no data was cached, or no data was scraped.
Using the echo feature in the debug tools, all I get is:
Document returned no data
The meta tags appear to be set up properly in my document:
<!DOCTYPE html>
<html>
<head prefix="og: http://ogp.me/ns# fb: http://ogp.me/ns/fb#">
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="robots" content="noarchive,noodp,noydir" />
<meta name="referrer" content="always" />
<meta property="fb:app_id" content="375576830972731" />
<meta property="og:site_name" content="Site Name" />
<meta property="og:title" content="Title of content" />
<meta property="og:url" content="https://website.domain.com/123456" />
<meta property="og:image" content="http://s3-us-west-1.amazonaws.com/domain/media/images/001/original/001" />
<meta property="og:description" content="Description of content" />
<meta property="og:type" content="article" />
</head>
I made sure to allow for the Facebook crawlers in robots.txt:
User-agent: Facebot
Allow: /
User-agent: facebookexternalhit/1.1
Allow: /
What I am I missing?
The problem here was my protect_from_forgerymethod in application.rb. I added an exception and the Facebot can now see the page.
class StoriesController < ApplicationController
protect_from_forgery except: :show
I have a base header layout (base-header-footer.gsp)
<!DOCTYPE html>
<html>
<head>
<title><g:layoutTitle default="${g.message(code: 'title.index.page')}"/></title>
</head>
... some common resources loading....
<body id="launch">
<g:layoutBody/>
...........................
<r:layoutResources />
</body>
</html>
And then 2 more header, one for logged-in user, and another for guest users, and both of these header layout are extending the base layout.
Guest users (anonymouys-header-footer.gsp) -
<g:applyLayout name="base-header-footer">
<!DOCTYPE html>
<html>
<head>
<g:layoutHead/>
</head>
<body>
... render guest user header
<g:layoutBody/>
</body>
</html>
Logged-in users (loggedin-header-footer.gsp) -
<g:applyLayout name="base-header-footer">
<!DOCTYPE html>
<html>
<head>
... some css
<g:layoutHead/>
</head>
<body>
... Render header for logged-in user
</body>
... load some JS file...
</html>
Now in specific pages I apply guest OR logged-in layout based on user's login state, hence I want to show the page specific title user is on, but it doesn't work.
This is how I am using those layout
OrderStatus.gsp -
<!DOCTYPE html>
<html>
<head>
<title>Order status | Some title</title>
<meta name="layout" content="logged-in-header-footer" />
<script type="text/javascript" src="${resource(dir:'js',file:'some.js')}"></script>
</head>
<body>
</body>
</html>
But I still see the title which is defined base-header-footer.gsp, not the one in OrderStatus.gsp
I have also tried using g:layoutTitle in OrderStatus.gsp but doesn't help.
Any help is highly appreciated.
Use
<meta name="layout" content="base-header-footer">
in your pages to load the layout, then add your title there,
<title>${whatever.something()}</title>
in your layout add this:
<title><g:layoutTitle/></title>
enjoy.
Try to use
<title><g:layoutTitle/></title>
in your layouts (base-header-footer and loggedin-header-footer.gsp). More info in the official documentation.
When I use #Html.Raw(mystring) normal it renders properly example:
#{ ViewBag.Title = "My Site®"; }
<title>#Html.Raw(ViewBag.Title)</title>
Will properly render <title>My Site®</title> but when I use it in an attribute:
<meta name="description" content="#Html.Raw(ViewBag.Title)" />
It renders <meta name="description" content="My Site®" /> which is not correct because then it will not render the registered mark.
How do you correct this behavior?
As patridge pointed out in his answer you can just include the attribute markup in the parameter to .Raw.
In your case that would result in something similar to the following:
<meta name="description" #Html.Raw("content=\"My Site®\"") />
You can do this in your _layout.cshtml:
<title>#{var title = new HtmlString(ViewBag.Title);}#title</title>
This worked for me.
I think it's the tip-top solution:
#{ ViewBag.Title = "My Site®"; }
<title>#Html.Raw(ViewBag.Title)</title>
<meta name="description" content="#HttpUtility.HtmlDecode(ViewBag.Title)" />
I've been battling various resource inclusion issues in my migration from Grails 1.3.7 from Grails 2.0, probably not understanding a few things to begin with.
Firstly, what does
<g:javascript library="application" />
do? (this was in the default main.gsp provided in Grails 1.3.7).
Secondly, for including jquery across my application, can I just do
<r:require module='jquery' />
<r:layoutResources />
in the top of my main sitemesh page that does the
<g:layoutHead />
...
<g:layoutBody />
and "be done with it", using the
<r:layoutResources />
a second time after the
<g:layoutBody />
Thanks
Yes I struggled a little with this at first too.
So firstly the <g:javascript library="application" /> refers to a module defined in a config/*.Resources.groovy file (default is config/ApplicationResources.groovy), inside that you have named modules, eg:
modules = {
application {
resource url: 'js/jquery/jquery-ui-1.8.15.custom.min.js', disposition: 'head'
}
}
Secondly by example a Grails2 main.gsp (cutdown a lot here):
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title><g:layoutTitle default="Grails"/></title>
<link rel="stylesheet" href="${resource(dir: 'css', file: 'main.css')}" type="text/css">
<link rel="stylesheet" href="${resource(dir: 'css', file: 'mobile.css')}" type="text/css">
<link rel="stylesheet" href="${resource(dir: 'css/redmond', file: 'jquery-ui-1.8.15.custom.css')}" type="text/css">
<g:layoutHead/>
<g:javascript library="jquery"/>
<r:require module="application"/>
<r:layoutResources/>
</head>
<body>
<g:layoutBody/>
<r:layoutResources/>
</body>
</html>
Hope that sets you in the right direction
I am getting the following XHTML validation warning in my ASP.NET MVC master page:
Validation (XHTML 1.0 Transitional): Element 'title' occurs too few times.
The title tag for the master page is included in the ContentPlaceHolder in the head tag as shown in the code below. The title tag in the ContentPlaceHolder is not taken into account when performing the validation, and I do not want to just add another one in the head tag because then I will be left with two title tags.
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<meta http-equiv="Content-Style-Type" content="text/css" />
<asp:ContentPlaceHolder ID="head" runat="server">
<title></title>
</asp:ContentPlaceHolder>
</head>
One work around that I have found is to use the following technique in the head tag:
<% if (false) { %>
<title></title>
<% } %>
Is this the best practice to resolve this warning? I am not a huge fan of adding the excess code just to pass validation warnings but I will live with it if there is not a better alternative.
Do this instead:
<head>
<title><asp:ContentPlaceHolder ID="title" runat="server">Default Page Title Here</asp:ContentPlaceHolder></title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<meta http-equiv="Content-Style-Type" content="text/css" />
<asp:ContentPlaceHolder ID="head" runat="server"></asp:ContentPlaceHolder>
</head>
Or as an alternate, set the title programattically from each page.
What's happening in your case is that when a new view is created, it creates empty content items which override the default content in the placeholders. If you remove the empty content blocks from the view, the default placeholder content will be used, but then you can't set the contents from the view. Using the code above you can override a default title from each view and include scripts, etc. in the head independently of each other.
Here possible solutions are
First solution is
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<meta http-equiv="Content-Style-Type" content="text/css" />
<asp:ContentPlaceHolder ID="head" runat="server">
//<title></title> - this line should be removed.
</asp:ContentPlaceHolder>
second solution is,
Check whether the head tag having attribute runat="server",if have not set runat prperty means not a problem else need to remove the runat tag.