Using templates/includes in Spring Boot - java

im 'developing' an application made using Spring Boot, i am not so sure how to manage templates.
I've tought two ways to do this:
Using custom tags and returning the value of the HTML/JSP file as a String and use it
Something like this
<th:header></th:header>
<!-- This would be the header (The implementation of this tag in Java) -->
<body>
Things that are not common
<th:footer></th:footer>
<!-- This would be the footer -->
</body>
The other way could be using includes, but im not so sure how to do it...
Not sure if theres another way of doing this using Spring. Hope that you could understand me.
Thanks you before hand :·).

You could use Thymeleaf. Then you can use
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head th:replace="common/header :: common-header" />
<body>
<div th:replace="common/navbar :: common-navbar" />
<your content here>
</body
</html>
Then you would have the common-header and common-navbar thymeleaf fragments in separate files like so (note the th:fragment in the common files. Also note that these files are under a common folder):
<head th:fragment="common-header">
<title>DevOps Buddy</title>
<!-- Bootstrap core CSS -->
<link th:href="#{/webjars/bootstrap/3.3.6/css/bootstrap.min.css}"
rel="stylesheet" media="screen" />
<!-- Custom styles for this template -->
<link type="text/css" th:href="#{/css/styles.css}" rel="stylesheet" media="screen"/>
And so
<div th:fragment="common-navbar" class="navbar navbar-inverse navbar-fixed-top" role="navigation">
<div class="container">
<div class="navbar-header">
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target=".navbar-collapse">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
</div>
<div class="collapse navbar-collapse">
<ul class="nav navbar-nav">
<li class="active"><a th:text="#{navbar.home.text}" href="/"></a></li>
<li><a th:text="#{navbar.about.text}" href="/about"></a></li>
<li><a th:text="#{navbar.contact.text}" href="/contact"></a></li>
</ul>
<ul class="nav navbar-nav navbar-right">
<li th:if="${#authorization.expression('!isAuthenticated()')}">
<a th:href="#{/login}" th:text="#{navbar.login.text}" />
</li>
<li th:if="${#authorization.expression('isAuthenticated()')}">
<form id="f" th:action="#{/logout}" method="post" role="form" class="navbar-form">
<button type="submit" th:text="#{navbar.logout.text}" class="btn btn-primary" />
</form>
</li>
</ul>
</div><!--/.nav-collapse -->
</div>
</div>

Related

Bootstrap components not rendering in Thymeleaf template when having more than one path segment

I have a Spring Boot Web MVC application where I'm using a Bootstrap 4 template and Thymeleaf as view.
I'm facing a problem where a drop-down navigation item is rendering without styles and scripts when the containing page is mapped in the controller under more than one path segment. If the path consists only of one segment the component works as intended.
Page containing the component "menu.html"
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta name="description" content="">
<meta name="author" content="">
<title>Menu</title>
<!-- Custom fonts for this template -->
<link href="/vendor/fontawesome-free/css/all.min.css" rel="stylesheet" type="text/css">
<link href="https://fonts.googleapis.com/css?family=Nunito:200,200i,300,300i,400,400i,600,600i,700,700i,800,800i,900,900i"
rel="stylesheet">
<!-- Custom styles for this template -->
<link href="/css/sb-admin-2.min.css" rel="stylesheet">
<!-- Custom styles for this page -->
<link href="/vendor/datatables/dataTables.bootstrap4.min.css" rel="stylesheet">
</head>
<body id="page-top">
<div id="wrapper">
<nav class="navbar navbar-expand navbar-light bg-white topbar mb-4 static-top shadow">
<ul class="navbar-nav ml-auto">
<!-- Drop-Down Component -->
<li class="nav-item dropdown no-arrow">
<a class="nav-link dropdown-toggle" href="#" id="userDropdown" role="button"
data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
<span class="mr-2 d-none d-lg-inline text-gray-600 small" th:text="${user.getName()}"></span>
<img class="img-profile rounded-circle" src="img/undraw_profile.svg" width="50" height="50">
</a>
<!-- Dropdown - User Information -->
<div class="dropdown-menu dropdown-menu-right shadow animated--grow-in" aria-labelledby="userDropdown">
<a class="dropdown-item" href="#" data-toggle="modal" data-target="#logoutModal">
<i class="fas fa-sign-out-alt fa-sm fa-fw mr-2 text-gray-400"></i>
Logout
</a>
</div>
</li>
</ul>
</nav>
<!-- Logout Modal-->
<div class="modal fade" id="logoutModal" tabindex="-1" role="dialog" aria-labelledby="logoutModalLabel"
aria-hidden="true">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="logoutModalLabel">Ready to Leave?</h5>
<button class="close" type="button" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
<div class="modal-body">Select "Logout" below if you are ready to end your current session.</div>
<div class="modal-footer">
<button class="btn btn-secondary" type="button" data-dismiss="modal">Cancel</button>
<a class="btn btn-primary" href="/login?logout=true">Logout</a>
</div>
</div>
</div>
</div>
</div>
<!-- Bootstrap core JavaScript-->
<script src="vendor/jquery/jquery.min.js"></script>
<script src="vendor/bootstrap/js/bootstrap.bundle.min.js"></script>
<!-- Core plugin JavaScript-->
<script src="vendor/jquery-easing/jquery.easing.min.js"></script>
<!-- Custom scripts for all pages-->
<script src="js/sb-admin-2.min.js"></script>
<!-- Page level plugins -->
<script src="vendor/datatables/jquery.dataTables.min.js"></script>
<script src="vendor/datatables/dataTables.bootstrap4.min.js"></script>
<!-- Page level custom scripts -->
<script src="js/demo/datatables-demo.js"></script>
</body>
</html>
Using the following mapping in my controller the component works fine:
#GetMapping(value = "/first-segment")
public String showModal(Model model) {
model.addAttribute("user", userService.getAuthenticatedUser());
return "menu";
}
But if I add a second segment no styling is displayed and the component can't be clicked on:
#GetMapping(value = "/first-segment/second-segment")
public String showModal(Model model) {
model.addAttribute("user", userService.getAuthenticatedUser());
return "menu";
}
Note
I'd also like to add that not all Bootstrap components don't render correctly under two path segments. As far as I've noticed only drop-down and modal components share this issue.
Replace:
<script src="vendor/jquery/jquery.min.js"></script>
with:
<script src="/vendor/jquery/jquery.min.js"></script>
Note the extra slash at the start. Otherwise, the Javascript file is supposed to be relative to the current file. That is why it worked for the first segment, but not the second segment.
By starting with a slash, it is always relative to the root of the path.

Adding button for each elements of list in thymeleaf

I'm having trouble putting a button into the element of the list based on Thymeleaf. I would like to have a button next to element of the list.
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org" xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
layout:decorate="~{layout.html}">
<head>
<meta charset="UTF-8">
<title>Home</title>
</head>
<body>
<section layout:fragment="" content="">
<div class="row">
<div class="col-2">
<a th:href="#{/archives}">archiwum zadań</a>
</div>
<div class="col-2">
<a th:href="#{/add}">dodawanie zadania</a>
</div>
</div>
<ul>
<li th:each="task: ${tasks}"
th:text="|${task.getId()} ${task.getName()} ${task.getCategory().getDescription()} ${task.isFinished()}|">
<input type="submit" value="Done">
</li>
</ul>
</section>
</body>
</html>
Thymeleaf will replace any pre-existing tag content with whatever is generated by the th:text expression.
In your case that pre-existing content is your <input> element - which is why the buttons are not displayed.
One way to avoid this is to place your th:text into a span inside the <li>:
<ul>
<li th:each="task: ${tasks}">
<span th:text="|${task.id} ${task.name} ... |"></span>
<input type="submit" value="Done">
</li>
</ul>
(I removed a couple of your fields from my example, for brevity).
Note also in my case, I have changed ${task.getId} to ${task.id}. As long as you have appropriately named getters (as per the JavaBeans naming convention) you can use the field name - and Thymeleaf will find the correct getter to call.

How to pass a value from href in HTML to Thymeleaf?

I am trying to get info from a input bar and use the value for a modal. Currently I am using Thymeleaf and I am getting everything mixed up. I tried setting the attribute for href using jQuery. Though I believe since the th:href gets rendered on page load, my code does not work.
The end goal is to send the info from the input field, process it in Thymeleaf, and then show the results in the modal. I can so far only accomplish it when I hard code everything. I am looking for a more dynamic way. Any help will be appreciated.
HTML:
<!DOCTYPE html>
<html xml="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta charset="ISO-8859-1">
<title>Application</title>
<link href="css/bootstrap.min.css" rel="stylesheet">
<link href="css/landing-page.css" rel="stylesheet">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.4.0/js/bootstrap.min.js"></script>
</head>
<body>
<div class="container-fluid">
<div class="row">
<div class="col-md-12">
<ul class="nav nav-tabs ">
<li class="nav-item">
<a class="nav-link active" href="#">Home</a>
</li>
<li class="nav-item">
<!-- <a class="nav-link" href="create.html">Create</a> -->
<a class="nav-link" href="/addTyler">Create</a>
</li>
<li class="nav-item dropdown ml-md-auto">
<a class="nav-link dropdown-toggle" href="http://example.com" id="navbarDropdownMenuLink" data-toggle="dropdown">Account</a>
<div class="dropdown-menu dropdown-menu-right" aria-labelledby="navbarDropdownMenuLink">
<a class="dropdown-item" href="#">Settings</a>
<div class="dropdown-divider">
</div> <a class="dropdown-item" href="#">Log out</a>
</div>
</li>
</ul>
</div>
</div>
<div class="row">
<div class="col-md-12">
<h3 class="text-center">
Taddm Mapping
</h3>
</div>
</div>
<div class="row" id="search-row">
<div class="col-md-8">
<div class="filter-search">
<input class="form-control" id="myInput" type="text" placeholder="Search..">
<div class="list-group" id="myList">
<div th:each="plants : ${plants}">
</div>
</div>
</div>
</div>
<div class="col-md-4">
<span class="input-group-btn">
<a class="btn btn-primary eBtn">View</a>
<!-- <a th:href="#{findOne/(appcode=01G)}" class="btn btn-primary eBtn">View</a> -->
</span>
</div>
<div class="myForm">
<form th:action="#{/save}" method="POST">
<div class="modal fade" id="exampleModal" tabindex="-1" role="dialog" aria-labelledby="exampleModalLabel" aria-hidden="true">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="exampleModalLabel">Update or Create</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
<div class="modal-body">
<div class="form-group">
<label for="appcode" class="col-form-label">AppCode:</label>
<input type="text" class="form-control" id="appcode" name="appcode" value=""/>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
<input type="submit" class="btn btn-primary" value="save"/>
</div>
</div>
</div>
</div>
</form>
</div>
</div>
</div>
</body>
</html>
jQuery:
// Empty JS for your own code to be here
$(document).ready(function() {
$("#myList").toggle();
$("#myInput").on("focus", function() {
$("#myList").toggle();
});
$("#myInput").on("focusout", function() {
// Here, it'll wait 100 miliseconds to hide the list.
setTimeout(function() {
$("#myList").toggle();
}, 250);
});
$("#myInput").on("keyup", function() {
var value = $(this).val().toLowerCase();
$("#myList a").filter(function() {
$(this).toggle($(this).text().toLowerCase().indexOf(value) > -1)
});
});
// This is the code to populate the field after selecting an option.
$("#myList a").on("click", function() {
var texto = $(this).text();
$("#myInput").val(texto);
});
$(".eBtn").on("click", function(event){
$("a").attr("href","#{findOne/(appcode='01G')}"); //Test
event.preventDefault();
var href = $(this).attr('href');
console.log(href);
$.get(href, function(appcode,status){
$("#appcode").val(appcode);
});
$("#exampleModal").modal();
});
});
Controller:
#RequestMapping("/findOne")
#ResponseBody
public String findOne(String appcode) {
System.out.println(mAppRepo.findById(appcode).get().getmAppName());
return mAppRepo.findById(appcode).get().getmAppName();
}

Send and include JSP dynamically in another JSP

I currently have a Jsp called menu-container that loads a base menu that should appear on each of my pages, but each page will add options to that menu, those options go in a specific place, so I would like to know how I can send the Content to the menu-container (which is a Jsp) from another, here is the menu-container:
<ul id="menu">
<!-- Modificar las authorities segun la aplicacion -->
<div class="navbar navbar-default menu">
<div class="navbar-header" >
<button type="button" class="navbar-toggle collapsed"
data-toggle="collapse" data-target="#menu-colapse" aria-
expanded="false"
aria-controls="navbar">
<span class="sr-only">Menú</span> <span class="icon-bar">
</span>
<span class="icon-bar"></span> <span class="icon-bar"></span>
</button>
</div>
<!-- Menu colapse -->
<div id="menu-colapse" class="navbar-collapse collapse">
<!-- Menu usuario - para opciones de control del usuario-->
<ul class="nav navbar-nav navbar-right">
<li class="dropdown user-options">
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">
<i class="fa fa-cog"></i>
<!--[if IE 7]>
Opciones <span class="caret"></span>
<![endif]-->
</a>
<!-- En este dropdown van las opciones que se necesiten en la aplicacion -->
<ul class="dropdown-menu">
<!-- Nombre del usuario, recuperado por spring -->
<li>
<a href="#" class="a-no-link">
<!--[if !IE 7]><!-->
<i class="fa fa-user fa-padding-6 fix-right-menu" aria-hidden="true"></i>
<!--<![endif]-->
<sec:authentication property="principal" />
</a>
</li>
<!-- Logout -->
<li>
<div class="form-inline" style="color: white !important; height: 23px;">
<!--[if !IE 7]><!-->
<i class="fa fa-sign-out fa-padding-6" aria-hidden="true" style="margin-left: 16px;"></i>
<!--<![endif]-->
<spring:message code="menuPpal.salir"/>
</div>
</li>
</ul>
</li>
</ul>
<ul class="nav navbar-nav navbar-right">
<li class="dropdown user-options">
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">
<i class="fa fa-language"></i>
</a>
<ul class="dropdown-menu">
<li><a class="a-link" href="configuracionInicio?idioma=en&pagina=inicio" title="English">English</a></li>
<li><a class="a-link" href="configuracionInicio?idioma=es&pagina=inicio" title="Español">Español</a></li>
<li><a class="a-link" href="configuracionInicio?idioma=fr&pagina=inicio" title="Français">Français</a></li>
</ul>
</li>
</ul>
<!-- /Menu usuario -->
<ul class="nav navbar-nav navbar-right" aria-expanded="false" id="main-menu">
//This is the place for the Li Items
//One or more items to add
</ul>
Now the invoking Jsp has something like this:
<jsp:include page="comun/menu-container.jsp" />
But as I must send it from the jsp that invokes the items (from which it invokes because it is in that they are distinguished from each other of course), but I do not know how to do it, suppose I put them in a new Jsp, that I call it and then How do I pass it to the container? And how do you receive it?
Thanks for taking the time any help is welcome, regards!
Pdta: what I want to avoid is having to have muuuchas classes menu that only change in what they add because it makes me very bad practice.
You can pass extra menu item as parameters from invoking JSP.
menu-container.jsp
<ul id="main-menu">
${param.menuItems}
</ul>
invoking_jsp.jsp
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Invoking JSP</title>
</head>
<body>
<jsp:include page="menu-container.jsp">
<jsp:param name="menuItems" value="<li>Special menu item 1</li><li>Special menu item 2</li>"/>
</jsp:include>
</body>
</html>
Edit : if you want to keep the menu items in a separate jsp page, as you suggest in the comments, you can just pass the jsp page name as the parameter as below.
menu-container.jsp
<ul id="main-menu">
<jsp:include page="${param.menuItems}"/>
</ul>
invoking_jsp.jsp
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Invoking JSP</title>
</head>
<body>
<jsp:include page="menu-container.jsp">
<jsp:param name="menuItems" value="menuPrincipal.jsp"/>
</jsp:include>
</body>
</html>

Missing end tag for "form:input"

I have error in first line of following jsp page
<%#taglib prefix="form" uri="http://www.springframework.org/tags/form"%>
<%#taglib prefix="spring" uri="http://www.springframework.org/tags"%>
<html>
<head>
<meta http-equiv="Content-Type"
content="text/html; charset=ISO-8859-
1">
<link rel="stylesheet"
href="//netdna.bootstrapcdn.com/bootstrap/3.0.0/css/boo
tstrap.min.css">
<title>Products</title>
</head>
<body>
<section>
<div class="jumbotron">
<div class="container">
<h1>Products</h1>
<p>Add products</p>
</div>
</div>
</section>
<section class="container">
<form:form modelAttribute="newProduct" class="form-horizontal">
<fieldset>
<legend>Add new product</legend>
<div class="form-group">
<label class="control-label col-lg-2 col-lg-2" for="productId">Product
Id</label>
<div class="col-lg-10">
<form:input id="productId" path="productId" type="text"
class="form:input-large" />
</div>
</div>
<!-- Similarly bind <form:input> tag for
name,unitPrice,manufacturer,category,unitsInStock and unitsInOrder
fields-->
<div class="form-group">
<label class="control-label col-lg-2" for="description">Description</label>
<div class="col-lg-10">form:textarea id="description"
path="description" rows = "2"/></div>
</div>
<div class="form-group">
<label class="control-label col-lg-2" for="discontinued">Discontinued</label>
<div class="col-lg-10">
<form:checkbox id="discontinued" path="discontinued" />
</div>
</div>
<div class="form-group">
<label class="control-label col-lg-2" for="condition">Condition</label>
<div class="col-lg-10">
<form:radiobutton path="condition" value="New" />
New
<form:radiobutton path="condition" value="Old" />
Old
<form:radiobutton path="condition" value="Refurbished" />
Refurbished
</div>
</div>
<div class="form-group">
<div class="col-lg-offset-2 col-lg-10">
<input type="submit" id="btnAdd" class="btn btn-primary"
value="Add" />
</div>
</div>
</fieldset>
</form:form>
</section>
</body>
</html>
while doing this jsp i got error, I am new to view part and have little to none knowledge of Jstl el but while learning spring I got stuck in this error which says
Multiple annotations found at this line:
> - Missing end tag for
> "form:input"
>- Missing end tag for
> "form:input"
Am I missing something, how to solve this? even a small hint is welcomed too
Thanks
el will also evaluate expressions in comments, so you have to make the tags in comments as it is in "normal" page
Change
<!-- Similarly bind <form:input> tag for
name,unitPrice,manufacturer,category,unitsInStock and unitsInOrder
fields-->
to
<!-- Similarly bind <form:input/> tag for
name,unitPrice,manufacturer,category,unitsInStock and unitsInOrder
fields-->

Categories