Include fragments in themeleaf layout based on condition using Spring Boot - spring-mvc

I am very newbie to Thymeleaf template engine, but i have working with other template engine like blade.
I want to include fragment based on condition like i have different menus for different users for example admin, manager, super user, user etc. I have keep these menus in headers and each header is in different fragment file like adminheader, defaultheader, userheader.html.
Now i want to check what whether user is logined or not. If not logined than display layout with defaultheader or if logined, than check user is admin or not. If user is admin than display page with adminheader otherwise display page page with userheader.
Efforts:
Till now page is opening with the default header and i have design layout file like below code,
layout.html
<!DOCTYPE html>
<html xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" xmlns="http://www.w3.org/1999/xhtml"
xmlns:th="http://www.thymeleaf.org">
<head layout:include="layouts/fragments/head :: headFragment">
</head>
<body>
<div class="container-fluid">
<header id="header" class="row navbar-fixed-top" layout:include="layouts/fragments/header :: headerFragment" >
</header>
<section class="row margin-top-120">
<div class="container-fluid">
<section id="main-content" class="col-lg-12" layout:fragment="content">
</section>
</div>
</section>
<footer id="footer" class="row">
<div class="container-fluid" layout:include = "layouts/fragments/footer :: footerFragment">
</div>
</footer>
</div>
</body>
</html>
I have included headerFragment in this layout file and working fine. Now don't know how to include other header fragment based on condition.
**Updated**
Controller
#Controller
public class IndexController {
private static final Logger logger = LoggerFactory.getLogger(IndexController.class);
#RequestMapping("/")
public String index(){
return "index";
}
#RequestMapping(value="/login", method=RequestMethod.GET)
public String login(User user) {
logger.info("Hello Get Login");
return "login";
}
#RequestMapping(value="/validateLogin", method=RequestMethod.POST)
public String validateLogin(#Valid #ModelAttribute("user") User user, BindingResult bindingResult) {
logger.info(user.toString());
if (bindingResult.hasErrors()) {
logger.info(bindingResult.toString());
return "login";
}
return "redirect:/";
}
#RequestMapping("/logout")
public String logout(){
return "index";
}
....
}
Please suggest me the solution.
Thanks.

You can use expressions for the include of different fragments on a condition. There is an example in the docs of thymeleaf.
In templatename :: domselector, both templatename and domselector can be fully-featured expressions. In the below example we want to include different fragments depending on a condition. If the authenticated user is an Admin, we will show a different footer than for a regular user:
<div th:replace="fragments/footer :: ${#authentication.principal.isAdmin()} ? 'footer-admin' : 'footer'">
© 2013 The Static Templates
</div>

Thanks #Patrick for your answer. But i have found the solution own.
I don't know whether it is good or not. Please tell.
layout.html
<!DOCTYPE html>
<html xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" xmlns="http://www.w3.org/1999/xhtml"
xmlns:th="http://www.thymeleaf.org">
<head layout:include="layouts/fragments/head :: headFragment"></head>
<body>
<div class="container-fluid">
((${session.containsKey('user')}) ?
<header id="header" class="row navbar-fixed-top" layout:include="layouts/fragments/admin-Header :: headerFragment">
</header> :
<header id="header" class="row navbar-fixed-top" layout:include="layouts/fragments/default-Header :: headerFragment">
</header>)
<section class="row margin-top-120">
<div class="container-fluid">
<section id="main-content" class="col-lg-12" layout:fragment="content">
</section>
</div>
</section>
<footer id="footer" class="row">
<div class="container-fluid" layout:include = "layouts/fragments/footer :: footerFragment">
</div>
</footer>
</div>
</body>
</html>

Related

Unable to display value which is returned from Spring controller

I'm trying to display like ${mesg}, it's not displaying the content, which is coming from the Spring controller. I have tried many ways, but no luck.
<html>
<head> <meta charset="ISO-8859-1">
<title>HOME</title>
</head>
<body>
<div align="center"> ${mesg} </div>
</body>
</html>
#RequestMapping(value="/savefile",method=RequestMethod.POST)
public String getStatus(#PathParam("pwd") String Pwd,ModelMap map){
System.out.println(":::pwd::"+Pwd);
map.addAttribute("mesg", "Welcome to mBOK");
return "Success";
}

Trying to ignore unused layout fragment in Thymeleaf Layout Dialect

Does anyone know if it's possible to hide a layout:fragment if it is not specified in the calling page?
For example, I have a page layout.html that has something like (where there is a separate fragment.html file with header and footer fragments):
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:th="http://www.thymeleaf.org"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
th:lang = "en">
<head>
<title layout:title-pattern="$CONTENT_TITLE">TITLE</title>
</head>
<body>
<header layout:replace="fragment :: header">HEADER</header>
<section layout:fragment="messages">MESSAGES</section>
<section layout:fragment="content">CONTENT</section>
<footer layout:replace="fragment :: footer">FOOTER</footer>
</body>
</html>
If in a calling page to the layout that I don't want to include the "messages" fragment, is there a way to do it by just not including that code? For example (say, simple.html):
<html layout:decorator="layout">
<head>
<title th:text=#{PAGETITLE_SIMPLE}>SIMPLE PAGE TITLE</title>
</head>
<body>
<section layout:fragment="content">
<p>Put in some random content for the body of the simple page</p>
</section>
</body>
This will still put into the rendered HTML the text "MESSAGES" inside a <section>-tag.
I have been able to put into this simple.html
<section layout:fragment="messages" th:remove="all"></section>
But this seems somewhat sloppy and was wondering if there was a way to hide that from the users of the layout by putting the logic in the layout to ignore that fragment altogether.
Using Spring 4.1.6, Thymleaf 2.1.4, and Layout Dialect 1.3.3.
Thanks
I was able to resolve this by applying the methods posted by Serge Ballesta in How to check Thymeleaf fragment is defined to the layout dialect.
This is what the rewritten layout.html looks like:
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:th="http://www.thymeleaf.org"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
th:lang = "en">
<head>
<title layout:title-pattern="$CONTENT_TITLE">TITLE</title>
</head>
<body>
<header layout:replace="fragment :: header">HEADER</header>
<section layout:replace="this :: messages">MESSAGES</section>
<section layout:fragment="content">CONTENT</section>
<footer layout:replace="fragment :: footer">FOOTER</footer>
</body>
</html>
This way, if the calling page (simple.html) only has the <section> for content, no HTML will be rendered for the section for messages. But if the page did have the following, it will be included as intended:
<section layout:fragment="messages">
<p>Message 1</p>
<p>Message 2</p>
</section>

How do I use dynamic content in thymeleaf fragments?

I have a spring project set up with thymeleaf and thymeleaf-layout-dialect.
In this project I have a controller
#Controller
public class HomeController {
#RequestMapping(value="/", method = RequestMethod.GET)
public String showHome(Model model){
return "home";
}
#RequestMapping(value="/info", method = RequestMethod.GET)
public String showInfo(Model model){
return "info";
}
}
I also have a layout
...
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:th="http://www.thymeleaf.org"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout">
<body>
<div layout:fragment="header">
dummy header
</div>
<div layout:fragment="content">
dummy contents
</div>
</body>
</html>
and two views: home.html and info.html which have different unique contents
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:th="http://www.thymeleaf.org"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
layout:decorator="layout">
<body>
<div layout:fragment="content">
unique contents
</div>
</body>
</html>
and a header
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:th="http://www.thymeleaf.org"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout">
<body>
<div layout:fragment="header">
username is ${username}
</div>
</body>
</html>
I'd like to print the username in the header without passing it as an attribute on the model in the controller methods. So, Can I run some custom java code before the header is included to find out the username regardless of what controller method is beeing used? What options do I have?
If you would use Spring Security in combination with the Thymeleaf Spring Security module, you can do the following:
<span class="user-info">
<small>Welcome,</small>
<span sec:authentication="principal">Username</span>
</span>
If you don't want to use Spring Security, you can write a custom dialect/processor which inserts the username for you.

Layout.css no longer being recognised after change in url for web application

Hi am using Tapestry 5 to create a planning web application and to get around a spring security issue I have changed the url pattern in org.apache.tapestry5.TapestryFilter from /* to /planning/*
After altering the corresponding urls and file paths in any Java / .tml files accordingly the web application works as itended, however my css stylesheet is no longer being picked up.
I have a Layout.java in a components file and a corresponding Layout.tml, and then a layout.css file in a layout folder in the webapp file (outside of WEB-INF)
The stylesheet is being called in the Layout.java file by #Import(stylesheet="context:layout/layout.css") - this always worked before!
I have tried moving the css file into a planning subdirectry planning/layout/layout.css but this still doesnt seem to make a difference!
Web.xml
<display-name>Planning Tapestry 5 Application</display-name>
<context-param>
<param-name>tapestry.app-package</param-name>
<param-value>com.quartetfs.planning.tapestry</param-value>
</context-param>
<filter>
<filter-name>app</filter-name>
<filter-class>org.apache.tapestry5.TapestryFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>app</filter-name>
<url-pattern>/planning/*</url-pattern>
</filter-mapping>
Layout.java
import org.apache.tapestry5.*;
import org.apache.tapestry5.annotations.*;
import org.apache.tapestry5.ioc.annotations.*;
import org.apache.tapestry5.BindingConstants;
/**
* Layout component for pages of Planning Application.
*/
#Import(stylesheet="context:layout/layout.css")
public class Layout
{
/** The page title, for the <title> element and the <h1> element. */
#Property
#Parameter(required = true, defaultPrefix = BindingConstants.LITERAL)
private String title;
#Property
private String pageName;
#Property
#Parameter(defaultPrefix = BindingConstants.LITERAL)
private String sidebarTitle;
#Property
#Parameter(defaultPrefix = BindingConstants.LITERAL)
private Block sidebar;
#Inject
private ComponentResources resources;
public String getClassForPageName()
{
return resources.getPageName().equalsIgnoreCase(pageName)
? "current_page_item"
: null;
}
public String[] getPageNames()
{
return new String[] { "planning/Index", };// "About", "Contact" }; TODO
}
}
Layout.tml
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1- strict.dtd">
<!--
Design by Free CSS Templates
http://www.freecsstemplates.org
Released for free under a Creative Commons Attribution 2.5 License
Title : Concrete
Version : 1.0
Released : 20080825
Description: A Web 2.0 design with fluid width suitable for blogs and small websites.
-->
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:t="http://tapestry.apache.org/schema/tapestry_5_1_0.xsd"
xmlns:p="tapestry:parameter">
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8"/>
<title>${title}</title>
</head>
<body>
<!-- start header -->
<div id="header">
<div id="logo">
<h1>
<t:pagelink page="planning/index">Project and Resource Planning</t:pagelink>
</h1>
</div>
<div id="menu">
<ul>
<li t:type="loop" source="pageNames" value="pageName" class="prop:classForPageName">
<t:pagelink page="prop:pageName">${pageName}</t:pagelink>
</li>
</ul>
</div>
</div>
<!-- end header -->
<!-- start page -->
<div id="page">
<!-- start sidebar -->
<div id="sidebar">
<ul>
<li id="search" style="background: none;">
</li>
<li t:type="if" test="sidebar">
<h2>${sidebarTitle}</h2>
<div class="sidebar-content">
<t:delegate to="sidebar"/>
</div>
</li>
</ul>
</div>
<!-- end sidebar -->
<!-- start content -->
<div id="content">
<div class="post">
<div class="title">
<h2>${title}</h2>
</div>
<div class="entry">
<t:body/>
</div>
</div>
</div>
<!-- end content -->
<br style="clear: both;"/>
</div>
<!-- end page -->
<!-- start footer -->
<div id="footer">
<p class="legal">
©2009 com.example. All Rights Reserved.
•
Design by
Free CSS Templates
•
Icons by
FAMFAMFAM.
</p>
</div>
<!-- end footer -->
</body>
</html>
You should not have to move the layout.css file; if Tapestry can't find it, then it will throw an exception when loading the pages containing the Layout component.
When you have a problem like this, it is very useful to view the source of the rendered page, and see what the requests and reponses are.
I suspect that the URL for the stylsheet is coming out as "/assets/..." ... which is not picked up by Tapestry (due to how your filter mapping is set up), and you'll see a 404 error.
It is necessary to inform Tapestry that you have placed the application in a sub-folder (alas, the Servlet API does not provide introspection into how you have things configured in your web.xml).
http://tapestry.apache.org/configuration.html#Configuration-tapestry.applicationfolder
Once Tapestry knows the application folder, it can build proper URLs.

How can I customize how the title is displayed?

I want to customize how the HTML for the title of my Dexterity content type is generated.
I wrote a view template for a the type that uses the metadata.IBasic behavior:
<html ...>
<body>
<metal:content-core fill-slot="content-core">
<metal:content-core define-macro="content-core">
<div id="conent-images">...</div>
...
<div id="content-metadata">
<h1 tal:content="context/title">Title</h1>
...
</div>
...
<div id="content-body">...</div>
</metal:content-core>
</metal:content-core>
</body>
</html>
But Plone then renders the title twice. How can I remove the first apparition of title?
With that code you are filling the slot named content-core. There are several slots defined in the layout that is the base for the template: content-title, content-description y content-core.
To remove the first title apparition you can fill the the content-title slot with nothing.
<html ...>
<body>
<metal:content-core fill-slot="content-title">
<metal:content-core define-macro="content-title">
</metal:conent-core>
</metal:conent-core>
<metal:content-core fill-slot="content-core">
<metal:content-core define-macro="content-core">
...
<h1 tal:content="context/title">Title</h1>
...
<div id="content-body">...</div>
</metal:content-core>
</metal:content-core>
</body>
</html>
Other solution is edit the template where slots are defined, but this solution is enough for me.

Resources