Thymeleaf dialect to create interface components, provides a simple way to define reusable components and some html attributes to render user interface (html) components.
See demo project: https://github.com/jpenren/thymeleaf-ui-dialect-demo
Add the Maven dependency to your project:
<dependency>
<groupId>io.github.jpenren</groupId>
<artifactId>thymeleaf-ui-dialect</artifactId>
<version>1.1.0</version>
</dependency>
This library uses Spring Boot autoconfiguration feature, if required you can define this dialect manually:
templateEngine.addDialect(new UiDialect());
String theme = "default";
templateEngine.addTemplateResolver(new ComponentTemplateResolver(theme));
Enabling this dialect will introduce the ui
namespace and a new set of attribute processors for available components:
Alert
Template:
<div ui:alert="#{msg.key}" ui:dismissible="true"></div>
Output:
<div class="alert alert-primary alert-dismissible fade show" role="alert">
Alert message
<button type="button" class="close" data-dismiss="alert" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
Badge
Template:
<span ui:badge="value"></span>
Output:
<span class="badge badge-secondary">value</span>
Template:
<span ui:badge="value" ui:href="'http://www.example.org'"></span>
Output:
<a class="badge badge-secondary" href="http://www.example.org">value</a>
Breadcrumb
Template:
<div ui:breadcrumb ui:locations="${ {#ui.location('http://url','Home'), #ui.location('Current page')} }"></div>
Output:
<nav aria-label="breadcrumb">
<ol class="breadcrumb">
<li class="breadcrumb-item">
<a href="http://url">Home</a>
</li>
<li class="breadcrumb-item active" aria-current="page">
Current page
</li>
</ol>
</nav>
Button
Template:
<button ui:button ui:toggle="true" ui:active="true"></button>
Output:
<button type="button" class="btn btn-primary active" data-toggle="button"></button>
Card
Template:
<div ui:card ui:header="Header" ui:image="${#ui.image('http://url','alt text','title')}" ui:footer="Footer"></div>
Output:
<div class="card">
<div class="card-header">Header</div>
<img class="card-img-top" src="http://url" alt="alt text">
<div class="card-body">
</div>
<div class="card-footer text-muted">Footer</div>
</div>
Carousel
Template:
<div ui:carousel ui:slides="${ {#ui.slide('#{http://url}','Caption'), #ui.slide('http://url')} }" ui:show-controls="true" ui:show-indicators="true"></div>
Output:
<div class="carousel slide" data-ride="carousel" id="car_1551028552310">
<ol class="carousel-indicators">
<li data-slide-to="0" data-target="#car_1551028552310" class="active"></li>
<li data-slide-to="1" data-target="#car_1551028552310"></li>
</ol>
<div class="carousel-inner">
<div class="carousel-item active">
<img class="d-block w-100" src="#{http://url}">
<div class="carousel-caption d-none d-md-block">??Caption_es_ES??</div>
</div>
<div class="carousel-item">
<img class="d-block w-100" src="http://url">
</div>
</div>
<a class="carousel-control-prev" href="#car_1551028552310" role="button" data-slide="prev">
<span class="carousel-control-prev-icon" aria-hidden="true"></span>
<span class="sr-only">Previous</span>
</a>
<a class="carousel-control-next" href="#car_1551028552310" role="button" data-slide="next">
<span class="carousel-control-next-icon" aria-hidden="true"></span>
<span class="sr-only">Next</span>
</a>
</div>
Dropdown
Template:
<div ui:dropdown="#{toggle}" ui:header="#{header}" ui:items="${ {#ui.link('href', 'text'), #ui.divider, #ui.link('rerf','tex')} }"></div>
Output:
<div class="dropdown">
<button class="btn btn-secondary dropdown-toggle" type="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">Toggle</button>
<div class="dropdown-menu">
<h6 class="dropdown-header">Header</h6>
<a class="dropdown-item" href="href">Text</a>
<div class="dropdown-divider"></div>
<a class="dropdown-item" href="rerf">Text</a>
</div>
</div>
Figure
Template:
<div ui:figure="${#ui.image('http://url')}" ui:caption="#{caption}"></div>
Output:
<figure class="figure">
<img src="http://url" class="figure-img img-fluid">
<figcaption class="figure-caption">Caption text</figcaption>
</figure>
Navigation
Template:
<div ui:nav ui:items="${ {#ui.link('href', 'text'),#ui.link('href','text')} }"></div>
Output:
<ul class="nav">
<li class="nav-item" >
<a class="nav-link" href="href">Text</a>
</li>
<li class="nav-item" >
<a class="nav-link" href="href">Text</a>
</li>
</ul>
Navigation header
Template:
<nav ui:nav-header ui:brand="${ #ui.link('href','text') }" ui:items="${ {#ui.link('ref','alt')} }"></nav>
Output:
<nav class="navbar navbar-light bg-light">
<a class="navbar-brand" href="href">Text</a>
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarSupportedContent">
<ul class="navbar-nav mr-auto">
<li class="nav-item">
<a class="nav-link" href="href">Text</a>
</li>
</ul>
</div>
</nav>
Example with Java code:
import static io.github.thymeleaf.ui.Components.*;
...
@ControllerAdvice
public class UiControllerAdvice {
@ModelAttribute
public NavigationHeader addNavigationHeader() {
NavigationHeader header = navigationHeader("#", "navheader", link("#", "text"));
Dropdown dropdown = dropdown("toggle", "t1");
dropdown.add(link("#", "text"));
dropdown.add(link("#", "text"));
dropdown.divider();
dropdown.add(link("#", "text"));
header.add(dropdown);
return header;
}
}
Template:
<nav ui:nav-header></nav>
Output:
<nav class="navbar navbar-light bg-light">
<a class="navbar-brand" href="href">Text</a>
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarSupportedContent">
<ul class="navbar-nav mr-auto">
<li class="nav-item">
<a class="nav-link" href="#">Text</a>
</li>
<li class="nav-item dropdown">
<a class="nav-link dropdown-toggle" id="t1" aria-expanded="false" href="#" role="button" data-toggle="dropdown" aria-haspopup="true">Toogle</a>
<div class="dropdown-menu" aria-labelledby="t1">
<a href="#" class="dropdown-item">Text</a>
<a href="#" class="dropdown-item">Text</a>
<div class="dropdown-divider"></div>
<a href="#" class="dropdown-item">Text</a>
</div>
</li>
<li class="nav-item">
<a class="nav-link" href="href">Text</a>
</li>
</ul>
</div>
</nav>
Sidebar
Template:
<nav ui:sidebar="mySidebar" ui:header="#{header}" ui:items="${ {#ui.link('href','text'), #ui.link('href','text')} }"></nav>
Output:
<nav class="sidebar-nav">
<div class="sidebar-header">Header</div>
<ul>
<li>
<a href="href">Text</a>
</li>
<li>
<a href="href">Text</a>
</li>
</ul>
</nav>
Exmple with Java code:
import static io.github.thymeleaf.ui.Components.*;
...
@ControllerAdvice
public class UiControllerAdvice {
@ModelAttribute
public Sidebar addSidebar() {
Sidebar sidebar = sidebar("Header", link("#", "text"), link("#", "text"));
sidebar.add(submenu("Menu", link("#", "text"), link("#", "text")));
return sidebar;
}
}
Template
<nav ui:sidebar></nav>
Output:
<nav class="sidebar-nav">
<div class="sidebar-header">Header</div>
<ul>
<li>
<a href="#">text</a>
</li>
<li>
<a href="#">text</a>
</li>
<li>
<a href="#menu_1551030034192" data-toggle="collapse" class="dropdown-toggle" aria-expanded="false">Menu</a>
<ul id="menu_1551030034192" class="sidebar-nav-menu collapse">
<li>
<a href="#">text</a>
</li>
<li>
<a href="#">text</a>
</li>
</ul>
</li>
</ul>
</nav>
Spring MVC @Controller:
import static io.github.thymeleaf.ui.Components.*;
...
@GetMapping("/index")
public String index(ModelMap model){
// Alert
model.addAttribute("alert", alert("This is an alert"));
//Badge
model.addAttribute("badge", badge("4"));
model.addAttribute("badge2", badge("5"));
//Breadcrumb
Breadcrumb breadcrumb = breadcrumb(location("/", "home"), location("/", "admin"), location("current-page"));
model.addAttribute(breadcrumb);
//Card
Card card = card("http://[image-location]", "Image alt");
card.setHeader("Card header");
card.setTitle("Card title");
card.setText("Some quick example text");
card.addLink("#", "Launch");
model.addAttribute(card);
//Carousel
Carousel carousel = carousel("demoCarousel", slide("http://[img-location]"), slide("http://img-location"));
carousel.setShowControls(true);
carousel.setShowIndicators(true);
model.addAttribute(carousel);
//Dropdown
Dropdown dropdown = dropdown("Toggle");
dropdown.add(link("#", "First Link"));
dropdown.setHeader("My header");
model.addAttribute(dropdown);
return "index";
}
Application html template:
<!doctype html>
<html lang="en" xmlns:th="http://www.thymeleaf.org" xmlns:ui="http://www.thymeleaf.org/ui">
<body>
<div ui:alert="${alert}"></div>
<span ui:alert="${badge}"></span>
<span class="badge badge-pill badge-primary" ui:alert="${badge2}"></span>
<nav ui:breadcrumb="${breadcrumb}"></nav>
<div ui:card="${card}"></div>
<div ui:carousel="${carousel}"></div>
<div ui:dropdown="${dropdown}"></div>
</body>
</html>
Note: the ui:render
tag is able to render any Renderable object. Using ui:render="${alert}"
produces the same output as ui:alert="${alert}"
Any component has an html template with some Thymeleaf logic, these templates are located under templates/ui/themes/[theme]/[class-name]-component.html
.
The default theme is default
based on Bootstrap 4.
Define your custom components just extending io.github.thymeleaf.ui.Component
and create the html template, an example of a new component:
public class CustomComponent extends Component {
private final String message;
public CustomComponent(String message) {
this.message = message;
}
public String getMessage() {
return message;
}
}
Template file for this component src/main/resources/templates/ui/themes/[theme]/custom-component-component.html
:
<div>
<strong th:text="*{message}"></strong>
</div>
Spring MVC @Controller
@GetMapping("/index")
public String index(ModelMap model) {
model.addAttribute(new CustomComponent("Hello world!"));
return "index";
}
Application html template:
<!doctype html>
<html lang="en" xmlns:th="http://www.thymeleaf.org" xmlns:ui="http://www.thymeleaf.org/ui">
<body>
<div ui:render="${customComponent}"></div>
</body>
</html>