Thymeleaf PDF template parsing error when using fragment - spring

I'm trying to include a letter head in my PDF templates like so:
fragments/header.html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Header</title>
</head>
<body>
<div id="header" class="clearfix" th:fragement="header(sender, name, address1, address2, today)">
<div id="header-left">
<img class="logo" src="app/src/main/resources/template/pdf/assets/logo.png"/>
<div id="recipient">
<p th:object="${sender}"><span th:text="*{name}"></span> - <span th:text="*{address1short}"></span> - <span th:text="*{address2}"></span></p>
<p th:text="${name}"></p>
<p>
<span th:text="${address1}"></span><br/>
<span th:text="${address2}"></span>
</p>
</div>
</div>
<div id="header-right" th:object="${sender}">
<p><strong th:text="*{name}"></strong></p>
<p>
<span th:text="*{address1}"></span><br />
<span th:text="*{address2}"></span><br />
<span class="label" th:text="*{phoneLabel} + ':'"></span><span th:text="*{phone}"></span><br />
<span class="label" th:text="*{faxLabel} + ':'"></span><span th:text="*{fax}"></span><br />
<span class="label" th:text="*{emailLabel} + ':'"></span><span th:text="*{email}"></span><br />
<strong><span th:text="*{internetLabel}"></span>: <span th:text="*{internet}"></span></strong>
</p>
<p>
<span th:text="*{court}"></span><br />
<span th:text="*{registryNr}"></span><br />
<span th:text="*{directorLabel}"></span>: <span th:text="*{director}"></span><br />
<span th:text="*{vatId}"></span><br />
</p>
<p><span th:text="*{todayPrefix}"></span> <span th:text="${today}"></span></p>
</div>
</div>
</body>
</html>
template.html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8"/>
<title>Template</title>
<link rel="stylesheet" type="text/css" media="all" href="app/src/main/resources/template/pdf/assets/style.css"/>
</head>
<body>
<div th:replace="app/src/main/resources/template/pdf/fragments/header :: header(${sender}, ${name}, ${address1}, ${address2}, ${today})"></div>
<!-- omitted for brevity -->
</body>
</html>
Everything works fine when I put the header code in the template directly. But when I try to inlude it as a fragment, like above, I get the following Error:
org.thymeleaf.exceptions.TemplateInputException: An error happened during template parsing (template: "app/src/main/resources/template/pdf/fragments/header.html" - line 9, col 6)
template.html is in the app/src/main/resources/template/pdf/ folder. I had to include css and image files with the full path (from the root of the project) to get them to work. I tried including the fragment with the full path (as above) and only fragements/header, but I get the same error each time.
Im using
spring-boot-starter-thymeleaf 2.1.1
thymeleaf 3.0.11
Stacktrace:
[removed bc of character limit]
UPDATE
The correct path for the fragment is (in my case) "template/pfd/fragment/header" (so, full path from resources folder). Also, there was a typo in th:fragement="header".

The fragment reference should not be absolute to your project, but relative to your thymeleaf template root. So instead of (assuming it is app/src/main/resources/template/ whre your thymeleaf templates are located)
th:replace="app/src/main/resources/template/pdf/fragments/header ..."
use
th:replace="pdf/fragments/header ..."

The common use is
<div th:replace="fragments/header :: header"><div>
Using Thymeleaf

Related

Image container blows up to whole page in bootstrap

I'm obviously doing something wrong but this is my Day 2 with Bootstrap. I have a fluid container with two rows. On the first row I've got a title and an image. That image - even though I reduced the height and have it set to responsive, causes that second column in the first row to basically extend to almost the whole page. When I remove the image, the world makes sense. When I put the image back, the container blows up. Happens with any image so it isn't the image, it is my bad bootstrap.
`<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>A basic website with Bootstrap #1</title>
<!-- Including Bootstrap here-->
<link href="https://cdn.jsdelivr.net/npm/bootstrap#5.3.0-alpha1/dist/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
<div class="container-fluid bg-secondary">
<div class="row">
<div class="col">
<h1 class="title-text text-light">Welcome to this Bootstrap Site #1</h1>
</div>
<div class="col">
<img src="masterchef.png" class="img-responsive h-25" alt="">
</div>
</div>
<div class="row">
<div class="col">
<h2 class="subtitle-text text-light">A site to learn Bootstrap well</h2>
</div>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap#5.3.0-alpha1/dist/js/bootstrap.bundle.min.js"></script>
</body>
</html>`
Tried using the thumbnail class, tried looking through Bootstrap docs, tried reordering to see if that helps. Did not help.
To fix these kinds of errors you need to learn how columns work in bootstrap:
You can read more about the columns here
The bootstrap columns are divided in this way, currently in your code you have entered only col which means they are columns that do not specify their size but fit the next columns;
Instead, in this example, the columns are expressed and you can command the display from mobile to desktop:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>A basic website with Bootstrap #1</title>
<!-- Including Bootstrap here-->
<link href="https://cdn.jsdelivr.net/npm/bootstrap#5.3.0-alpha1/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-GLhlTQ8iRABdZLl6O3oVMWSktQOp6b7In1Zl3/Jr59b6EGGoI1aFkw7cmDA6j6gD" crossorigin="anonymous">
<script src="https://cdn.jsdelivr.net/npm/bootstrap#5.3.0-alpha1/dist/js/bootstrap.bundle.min.js" integrity="sha384-w76AqPfDkMBDXo30jS1Sgez6pr3x5MlQ1ZAGC+nuZB+EYdgRZgiwxhTBTkF7CXvN" crossorigin="anonymous"></script>
</head>
<body>
<div class="container-fluid bg-secondary">
<div class="row">
<div class="col-6 col-md-12">
<h1 class="title-text text-light">Welcome to this Bootstrap Site #1</h1>
</div>
<div class="col-6 col-md-12">
<img src="https://e7.pngegg.com/pngimages/526/683/png-clipart-take-out-chef-s-uniform-cafe-yuva-indian-cuisine-master-chef-thumbnail.png" class="img-responsive" alt="">
</div>
</div>
<div class="row">
<div class="col-12">
<!--col-12 means that on all devices it will be 100% width magnitude-->
<h2 class="subtitle-text text-light">A site to learn Bootstrap well</h2>
</div>
</div>
</div>
<!--<div class="container-fluid menu-options">
<div class="col">
<button type = "button" class="btn mx-1 btn-primary">Home</button>
<button type = "button" class="btn mx-1 btn-primary">Men's</button>
<button type = "button" class="btn mx-1 btn-primary">Women's</button>
<button type = "button" class="btn mx-1 btn-primary">Clearance</button>
</div>
</div>
-->
</body>
</html>
(Did you see that? with the simple addition of the md class, but there are many others!)
In this case I inserted two columns in the row, so that from mobile the view would be split in two and after the tablet (md) would display
read more about bootstrap breakpoints
I got help offline. The main issue isn’t with Bootstrap, but with how percentages work. I had the h-25 class on the element. The h-25 class attempts to set the height of an element to 25% of its parent. However, percentages only work properly when the parent has a well-defined/fixed height. When the parent doesn’t have a well-defined height, the browser enters a kind of feedback loop as it tries to adjust the set the height to a percentage while also adjusting the parent height to contain it.
Solutions
The easiest solution is to remove “h-25” and set a fixed height on the
Another option is to set a fixed height on the parent so that the percentage can be calculated definitively.
I went with the first option as putting a size on the image made it responsive where it doesn't overflow the container on smaller viewports.

laravel #include with #section in <head>

I've a simple main layout:
<!DOCTYPE html>
<html>
<head>
<title>Title</title>
#yield('style')
</head>
<body>
#include('layouts.frontend.partials.slider')
#yield('javascript')
</body>
</html>
layouts.frontend.partials.slider
#section('style')
<link rel="stylesheet" type="text/css" href="mystyle.css">
#append
#section('javascript')
<script></script>
#append
<div class="swiper-container">
<div class="swiper-wrapper">
<div class="swiper-slide" data-swiper-autoplay="1000">Slide 1</div>
<div class="swiper-slide" data-swiper-autoplay="1000">Slide 2</div>
<div class="swiper-slide" data-swiper-autoplay="1000">Slide 3</div>
</div>
<div class="swiper-pagination"> </div>
<div class="swiper-button-prev"> </div>
<div class="swiper-button-next"> </div>
</div>
The #section('style') will be ignored while the #section('javascript') is working fine within the include file...
I've reduced both files (main and include) to a minimum and swapped the position of style and javascript without any difference
What seems to be working is to change to position from the #yield('style') to the body, like:
<!DOCTYPE html>
<html>
<head>
<title>Title of the document</title>
</head>
<body>
#include('layouts.frontend.partials.slider')
#yield('javascript')
#yield('style')
</body>
Maybe it's not allowed to have #section in an include file?
What i want to archive is to have multiple partial includes with it's own css and javascript includes
Thanks
Here is another way to do, it's good because you have some flexibility.
You create a master template, put your main files
master.blade.php
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<link rel="stylesheet" href="">
#section('css')
<!-- some master css here -->
#show
</head>
<body>
#section('navbar')
#include('common.navbar')
#show
#yield('content')
#include('common.footer')
#section('js')
<!-- some js here -->
#show
</body>
</html>
The others childs extends the master, you'll have all your layout and can customize what you want:
Child blade
#extends('master')
#section('css')
#parent
<!-- more css -->
#endsection
#section('navbar')
#parent
#endsection
#section('content')
<!-- Main content goes here -->
#endsection
#section('js')
<!-- replace js and add my own -->
<!-- others js -->
#endsection
Did you try extending your child blade file to use the master template?
At the top of layouts.frontend.partials.slider did you put #extends('layout.master') (or whatever the path to your master template is?)
It could be an issue caused by the order in which you are including files. Wouldn't a simpler solution be to have a #yield('slider') in your master template and simply wrap the slider content in #section('slider') and drop the #include...?

Thymeleaf + spring dynamic replace

Is it possible to create a dynamic replace in Thymeleaf?
I have the following controller:
#Controller
public class LoginController {
#RequestMapping("/login")
public String getLogin(Model model){
model.addAttribute("template","login");
return "index";
}
}
And the following view:
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org" >
<head></head>
<body>
<div th:replace="fragments/${template} :: ${template}"></div>
</body>
</html>
And i'm getting the following error:
Error resolving template "fragments/${template}", template might not exist or might not be accessible by any of the configured Template Resolvers
UPDATE
I tried to preprocess my variables like this:
<div th:replace="fragments/${__#{${template}}__} :: ${__#{${template}}__}"></div>
How ever now ${template} is getting replaced with login i have the following error now:
Exception evaluating SpringEL expression: "??login_en_US??"
Although Joe Essey's solution is working as well i solved with following code:
<div th:replace="#{'fragments/' + ${template}} :: ${template}"></div>
I believe the appropriate method to manage this behavior in thymeleaf is to use layout:fragment tags. Please correct me if I'm wrong. Here is a simple example of my layout page, and the login page which is 'dynamically' loaded:
layout.html
<html xmlns:layout="http://www.w3.org/1999/xhtml" xmlns:th="http://www.w3.org/1999/xhtml">
<head>
<title layout:title-pattern="$DECORATOR_TITLE - $CONTENT_TITLE">Layout</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<meta http-equiv="X-UA-Compatible" content="IE=Edge"/>
</head>
<body>
<div>
<div class="app-container">
<div th:fragment="content">
</div>
</div>
</div>
<div th:fragment="script"></div>
</body>
</html>
Then, when login gets loaded, it replaces the th:fragment div with the associated div in the html view which matches the string returned by the controller method, in this case login.html:
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org"
xmlns:layout="http://www.w3.org/1999/xhtml"
layout:decorator="layout">
<head>
<title>Login</title>
</head>
<body>
<div th:fragment="content">
<form th:action="#{/login}" method="post">
<div><label> User Name : <input type="text" name="username"/> </label></div>
<div><label> Password: <input type="password" name="password"/> </label></div>
<div><input type="submit" value="Sign In"/></div>
</form>
</div>
</body>
</html>
Now, if you want to load another fragment conditionally, the approach I take is to add replace tags with th:if cases. Here's an example of a Form that displays different questions based on an attribute of the current user:
<div th:if="${foo.type)} == 'type_1'">
<div th:replace="fragments/custom-questions :: type-1-checkboxes"></div>
</div>
<div th:if="${foo.type} == 'type_2'">
<div th:replace="fragments/custom-questions :: type-2-checkboxes"></div>
</div>
Then the associated div gets loaded from the file custom-questions.html:
<div th:fragment="type-1-checkboxes">
//stuff
</div>
<div th:fragment="type-2-checkboxes">
//stuff
</div>
I am just encountering this issue (this is my first time with thymeleaf/spring). This is what solved it for me:
<div class="col-md-12" th:include="__${template}__ :: body" ...
In Thymeleaf 3.0, the following solution has worked for me:
<div th:replace="('fragments/' + ${template}) :: (${template})">
(Note however, that I use it with fixed name of the fragment and dynamic name of the template, so the parantheses around :: (${template}) might be optional.)
The solution is inspired by documentation for Thymeleaf in https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html#fragment-specification-syntax
Both templatename and selector in the above examples can be fully-featured expressions (even conditionals!) like:
<div th:insert="footer :: (${user.isAdmin}? #{footer.admin} : #{footer.normaluser})"></div>
Note again how the surrounding ~{...} envelope is optional in th:insert/th:replace
<div th:insert=“${subpage}::fragementName”>
Just change subpage names and you will dynamic behaviour in thymleaf

Html Agility Pack, couldn't SelectNodes

Here's the HTML file, it's truncated due to the content size and contained with an "img" tag at the end.
<?xml version="1.0" encoding="UTF-8"?>
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"
xmlns:epub="http://www.idpf.org/2007/ops">
<head>
<meta charset="utf-8"></meta>
<title>The Waste Land</title>
<link rel="stylesheet" type="text/css" href="wasteland.css" class="day" title="day"/>
<link rel="alternate stylesheet" type="text/css" href="wasteland-night.css" class="night" title="night"/>
</head>
<body>
<section epub:type="frontmatter" id="frontmatter">
<section epub:type="titlepage" id="titlepage">
<h1>The Waste Land</h1>
<div class="aut">T.S. Eliot</div>
<div epub:type="epigraph">
</div>
<p epub:type="dedication">For Ezra Pound: <span xml:lang="it">il migliorfabbro</span></p>
</section>
</section>
<section epub:type="bodymatter" id="bodymatter">
<section id="ch1">
<h2>I. THE BURIAL OF THE DEAD</h2>
<div>Looking into the heart of light, the silence.</div>
<div xml:lang="de" id="ln42">
<em>Od' und leer das Meer</em>.<a epub:type="noteref" class="noteref" href="#note-4">*</a>
</div>
</div>
<div class="linegroup">
<img src="www.href">The lady of situations.
</img>
<div>Here is the man with three staves, and here the Wheel,</div>
<div>And here is the one-eyed merchant, and this card,</div>
</div>
</section>
</body>
Here's my code
HtmlNodeCollection imgNodes= HTMLDoc.DocumentNode.SelectNodes("//img");
or
List<HtmlNode> imgNodes = HTMLDoc.DocumentNode.Descendants().Where(n => n.Name =="img").ToList();
Neither can retrieve any tags. What can i do?

Visual Studio HTML5 Validation: Element 'img' cannot be nested within element 'a'

I've run into this curious validation error in Visual Studio 2010 with HTML5 validation selected:
Is this a validation error (legitimate or a bug in Visual Studio) or am I missing something obvious and simple?
Edit: added relevant code.
View cshtml:
#model My.Web.ViewModels.ListVideos
#{
ViewBag.Title = "All Videos";
}
#foreach (var item in Model.Videos) {
<a href="#Url.Action("Play", "Player", new { videoId = item.VideoId })">
<img src="http://i2.ytimg.com/vi/#item.PublisherVideoId/default.jpg" alt="#item.Title" style="border: 0" />
</a>
}
_Layout.cshtml:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>#ViewBag.Title</title>
<link href="#Url.Content("~/Content/Site.css")" rel="stylesheet" type="text/css" />
<script src="#Url.Content("~/Scripts/jquery-1.6.4.min.js")" type="text/javascript"></script>
<script src="#Url.Content("~/Scripts/modernizr-1.7.min.js")" type="text/javascript"></script>
</head>
<body>
<div class="page">
<header>
<div id="title">
<h1>My Web</h1>
</div>
<div id="logindisplay">
#Html.Partial("_LogOnPartial")
</div>
<nav>
<ul id="menu">
<li>#Html.ActionLink("Videos", "ListVideos", "Player")</li>
<li>#Html.ActionLink("Dev", "Index", "Dev")</li>
</ul>
</nav>
</header>
<section id="main">
#RenderBody()
</section>
<footer>
</footer>
</div>
</body>
</html>
You need visual studio 2010 SP1 web standards update to have this work properly. It is a bug.
See more here: http://blogs.msdn.com/b/webdevtools/archive/2011/06/15/web-standards-update-for-visual-studio-2010-sp1.aspx?PageIndex=2
Get the web standards update here:
http://visualstudiogallery.msdn.microsoft.com/a15c3ce9-f58f-42b7-8668-53f6cdc2cd83
If you check the W3C documentation about Html5 you will find that you can have inside a a tag anything if it's not button or other links.
The a element may be wrapped around entire paragraphs, lists, tables, and so forth, even entire sections, so long as there is no interactive content within (e.g. buttons or other links).

Resources