Keywords: Thymeleaf | Spring MVC | JavaScript | Template Engine | Data Passing
Abstract: This article provides an in-depth exploration of how to securely pass backend model data to frontend JavaScript variables in Spring MVC applications using the Thymeleaf template engine. Based on official documentation and best practices, it thoroughly examines core concepts such as Thymeleaf's JavaScript inlining mechanism, expression syntax, and escaping handling. Through complete code examples, it demonstrates the entire process from basic implementation to advanced usage. The content covers Thymeleaf's template modes, standard dialect features, advantages of natural templating, and practical considerations in real-world development, offering a complete and reliable solution for developers.
Introduction
In modern web application development, data interaction between frontend and backend is a core aspect. Spring MVC, as a widely used Java web framework, is often combined with the Thymeleaf template engine for server-side rendering. However, developers frequently face the challenge of passing backend model data to frontend JavaScript. Directly using approaches like var m = ${message}; not only fails to work but also leads to syntax errors and security risks. This article, based on Thymeleaf official documentation and community best practices, systematically explains how to achieve this goal through Thymeleaf's JavaScript inlining feature.
Thymeleaf and JavaScript Inlining Mechanism
Thymeleaf supports multiple template modes, including HTML, XML, JAVASCRIPT, etc. Among these, the JAVASCRIPT mode is specifically designed for processing JavaScript files, allowing the use of model data within scripts. However, in HTML templates, we more commonly use JavaScript inlining (th:inline="javascript") to embed dynamic content.
Basic Implementation
According to the official documentation, the correct approach is to add the th:inline="javascript" attribute to the <script> tag and use the /*[[${expression}]]*/ syntax. Here is a complete example:
<script th:inline="javascript">
/*<![CDATA[*/
var message = /*[[${message}]]*/ 'default';
console.log(message);
/*]]>*/
</script>
Here, /*[[${message}]]*/ will be replaced by Thymeleaf with the value of the model variable message. If message is undefined, the default value 'default' will be used. The CDATA section ensures that browsers ignore the inline code when viewing statically, maintaining template readability.
In-Depth Syntax and Features
Expression Processing and Escaping
Thymeleaf's expression syntax is based on OGNL (or SpringEL) and supports various types such as variables, messages, and links. In JavaScript inlining, expression results are automatically JavaScript-escaped to prevent XSS attacks. For example, if message has the value Hello "World", the output will be var message = "Hello \"World\"";, ensuring string safety.
Advanced Data Type Support
Thymeleaf can handle not only strings but also complex types like numbers, booleans, arrays, and collections. For instance, passing a list:
<script th:inline="javascript">
var items = /*[[${itemList}]]*/ [];
</script>
If itemList is ["A", "B"], the output will be var items = ["A", "B"];. Thymeleaf uses built-in serialization mechanisms (such as Jackson) for object conversion.
Natural Templating and Prototyping
Thymeleaf emphasizes the concept of natural templates, allowing templates to be rendered directly by browsers when unprocessed. Through inline comments, both dynamic and static views can be supported:
<script th:inline="javascript">
var user = /*[[${session.user}]]*/ { name: "Test User" };
</script>
In static mode, the browser displays Test User; during processing, it is replaced with actual data. This enhances collaboration between design and development.
Complete Example and Code Analysis
The following is a complete example integrating a Spring controller and Thymeleaf template, demonstrating how to pass multiple variables:
Spring Controller
@RequestMapping(value = "/data", method = RequestMethod.GET)
public String getData(Model model) {
model.addAttribute("message", "Hello from Spring!");
model.addAttribute("userCount", 42);
model.addAttribute("isActive", true);
return "data-page";
}
Thymeleaf Template (data-page.html)
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<title>Data Page</title>
</head>
<body>
<script th:inline="javascript">
/*<![CDATA[*/
var appData = {
message: /*[[${message}]]*/ 'default message',
count: /*[[${userCount}]]*/ 0,
active: /*[[${isActive}]]*/ false
};
console.log(appData.message); // Output: Hello from Spring!
console.log(appData.count); // Output: 42
console.log(appData.active); // Output: true
/*]]>*/
</script>
</body>
</html>
This example shows how to construct a JavaScript object with multiple properties, ensuring type safety and data integrity.
Common Issues and Solutions
Incorrect Usage Comparison
Many developers attempt to use var m = ${message}; directly, which causes JavaScript syntax errors because Thymeleaf expressions are not properly parsed. The correct method always relies on the inlining attribute.
Performance Optimization
Thymeleaf's template caching mechanism can significantly improve performance. By configuring the cacheable property of TemplateResolver, repeated parsing can be avoided. For example:
templateResolver.setCacheable(true);
templateResolver.setCacheTTLMs(3600000L); // Cache for 1 hour
Extended Application Scenarios
Dynamic Script Generation
Combining Thymeleaf's conditional statements, dynamic script generation can be achieved:
<script th:inline="javascript">
<th:block th:if="${user.isAdmin}">
console.log("Admin user detected");
</th:block>
</script>
Internationalization Support
Through message expressions, internationalization of JavaScript content can be easily implemented:
<script th:inline="javascript">
var welcomeMsg = /*[[#{welcome.message}]]*/ "Welcome";
</script>
Conclusion
Thymeleaf's JavaScript inlining feature provides a secure and efficient way to pass Spring model data to the frontend. By understanding its syntax mechanisms and best practices, developers can build maintainable and performance-optimized web applications. The methods introduced in this article not only solve basic data transfer issues but also support complex type handling and internationalization, making them suitable for various real-world development scenarios.