1. Introduction
In this article, we will present the Thymeleaf Markdown Editor component. Markdown is a lightweight markup language using plain-text-formatting syntax. This language is often used to format README.md files in projects, for writing comments, and to create a blog post using a simple plain text editor.
Check the following links for more useful articles about Thymeleaf engine:
Thymeleaf Articles
Thymeleaf Forms
2. Dependencies
2.1. Maven dependencies
To present how the Markdown Editor component works with Thymeleaf we used a simple Spring Boot application created as a Maven project with following dependencies:
2.2. Front-End libraries
bootstrap v4.0.0 - a frontend framework for creating responsive websites,
simplemde v1.11.2 - a JavaScript library for Markdown editor.
Project Maven pom.xml
file has the following structure:
Copy
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<artifactId>thymeleaf-bootstrap-markdown-editor</artifactId>
<properties>
<bootstrap.version>4.0.0-2</bootstrap.version>
<webjars-locator.version>0.30</webjars-locator.version>
<lombok.version>1.18.2</lombok.version>
<font-awesome.version>5.11.2</font-awesome.version>
<commonmark.version>0.13.1</commonmark.version>
</properties>
<!-- Inherit defaults from Spring Boot -->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.5.RELEASE</version>
</parent>
<!-- Add typical dependencies for a web application -->
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.webjars</groupId>
<artifactId>bootstrap</artifactId>
<version>${bootstrap.version}</version>
</dependency>
<dependency>
<groupId>org.webjars</groupId>
<artifactId>webjars-locator</artifactId>
<version>${webjars-locator.version}</version>
</dependency>
<dependency>
<groupId>org.webjars</groupId>
<artifactId>font-awesome</artifactId>
<version>${font-awesome.version}</version>
</dependency>
<dependency>
<groupId>com.atlassian.commonmark</groupId>
<artifactId>commonmark</artifactId>
<version>${commonmark.version}</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
<scope>provided</scope>
</dependency>
</dependencies>
<!-- Package as an executable jar -->
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
3. Model, Controller, and Main Application class
The sample application will simulate creating a blog post, so the main class in the model layer will contain fields like title
and content
:
Copy
package com.frontbackend.thymeleaf.bootstrap.model;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
@Setter
@Getter
@NoArgsConstructor
public class Post {
private String title;
private String content;
private String html;
}
GET and POST requests on the root context are handled by a simple Spring Web Controller with the following structure:
Copy
package com.frontbackend.thymeleaf.bootstrap.controller;
import org.commonmark.node.Node;
import org.commonmark.parser.Parser;
import org.commonmark.renderer.html.HtmlRenderer;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import com.frontbackend.thymeleaf.bootstrap.model.Post;
@Controller
@RequestMapping({ "/", "/index" })
public class IndexController {
@GetMapping
public String main(Model model) {
model.addAttribute("post", new Post());
return "index";
}
@PostMapping
public String save(Post post, Model model) {
post.setHtml(markdownToHTML(post.getContent()));
model.addAttribute("post", post);
return "saved";
}
private String markdownToHTML(String markdown) {
Parser parser = Parser.builder()
.build();
Node document = parser.parse(markdown);
HtmlRenderer renderer = HtmlRenderer.builder()
.build();
return renderer.render(document);
}
}
The Parser
class is used to parse the submitted markdown syntax. HtmlRenderer.render
method creates HTML document using parsed markdown as an input. The rendered HTML code will be placed in the html
field in the Post
object.
The Application
is the Java class with the main method that starts the Spring Boot application server:
Copy
package com.frontbackend.thymeleaf.bootstrap;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
4. Templates
The presentation layer contains two Thymeleaf templates:
index.html - a view that presents the Markdown editor component,
saved.html - here we are going to present rendered results.
The index.html
template has the following structure:
Copy
<!DOCTYPE HTML>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8"/>
<title>Spring Boot Thymeleaf Application - Bootstrap Markdown Editor</title>
<link th:rel="stylesheet" th:href="@{/webjars/bootstrap/4.0.0-2/css/bootstrap.min.css} "/>
<link th:rel="stylesheet" th:href="@{/webjars/font-awesome/5.11.2/css/all.css} "/>
<link th:rel="stylesheet" th:href="@{/assets/simplemde/simplemde.min.css}"/>
</head>
<body>
<!-- Navigation -->
<nav class="navbar navbar-expand-lg navbar-dark bg-dark static-top">
<div class="container">
<a class="navbar-brand" href="/">Thymeleaf - Bootstrap Markdown Editor</a>
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarResponsive"
aria-controls="navbarResponsive"
aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarResponsive">
<ul class="navbar-nav ml-auto">
<li class="nav-item active">
<a class="nav-link" href="#">Home
<span class="sr-only">(current)</span>
</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#">About</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#">Services</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#">Contact</a>
</li>
</ul>
</div>
</div>
</nav>
<div class="container">
<div class="row mt-5">
<div class="col">
<form method="post" th:object="${post}">
<div class="form-group">
<label for="title">Title:</label>
<input type="text" id="title" placeholder="Title" autocomplete="off" class="form-control"
th:field="*{title}"/>
</div>
<div class="form-group">
<label for="content">Content:</label>
<textarea type="text" rows="4" id="content" placeholder="Content" class="form-control"
th:field="*{content}" autocomplete="off"></textarea>
</div>
<button class="btn btn-primary" type="submit">Submit form</button>
</form>
</div>
</div>
</div>
<script th:src="@{/webjars/jquery/jquery.min.js}"></script>
<script th:src="@{/webjars/popper.js/umd/popper.min.js}"></script>
<script th:src="@{/webjars/bootstrap/js/bootstrap.min.js}"></script>
<script th:src="@{/assets/simplemde/simplemde.min.js}"></script>
<script>
var simplemde = new SimpleMDE({element: document.getElementById("content")});
</script>
</body>
</html>
We used https://github.com/sparksuite/simplemde-markdown-editor library for WYSIWYG-like editor that allows users who are less experienced with Markdown to use familiar toolbar buttons and shortcuts.
To initialize the library we used the following JavaScript code:
Copy
var simplemde = new SimpleMDE({element: document.getElementById("content")});
The saved.html
file has the following structure:
Copy
<!DOCTYPE HTML>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8"/>
<title>Spring Boot Thymeleaf Application - Bootstrap Markdown Editor</title>
<link th:rel="stylesheet" th:href="@{webjars/bootstrap/4.0.0-2/css/bootstrap.min.css} "/>
</head>
<body>
<!-- Navigation -->
<nav class="navbar navbar-expand-lg navbar-dark bg-dark static-top">
<div class="container">
<a class="navbar-brand" href="/">Thymeleaf - Bootstrap Markdown Editor</a>
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarResponsive"
aria-controls="navbarResponsive"
aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarResponsive">
<ul class="navbar-nav ml-auto">
<li class="nav-item active">
<a class="nav-link" href="#">Home
<span class="sr-only">(current)</span>
</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#">About</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#">Services</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#">Contact</a>
</li>
</ul>
</div>
</div>
</nav>
<div class="container">
<div class="col-sm-12 mt-5">
<h1 th:text="${post.title}">Title</h1>
<div th:utext="${post.html}">Content</div>
<a th:href="@{/}" class="btn btn-primary">Go back</a>
</div>
</div>
</body>
</html>
5. The output
The running application is available under http://locahost:8080
URL and presents the following functionality:
6. Conclusion
In this article, we presented the Thymeleaf Markdown Editor component. Markdown language is getting more popular in the programming world. Developers loved the simplicity and often use it as the main tool for creating project-related documents.
As usual, the code used in this article is available under our GitHub repository .
{{ 'Comments (%count%)' | trans {count:count} }}
{{ 'Comments are closed.' | trans }}