Search code examples
javascriptjavaspring-bootthymeleafspring-thymeleaf

Thymeleaf inline JavaScript is completely ignored


I'm trying to include a basic vanilla JS script into a Thymeleaf view but it seems to be completely ignored - I don't even see it when I inspect the page. I assume the issue is related with the fact that the view is part of a layout template but I cannot figure out where the problem is.

The View:

<!doctype html>
<html th:replace="~{fragments/layout :: layout(~{::title}, ~{}, ~{}, ~{::main})}">
    <head>
        <title>Reports Page</title>
    </head>     
    <body>
        <main role="main" class="flex-shrink-0">
            <!-- some HTML code here -->
            <select class="form-select" id="year" aria-label="Year dropdown" th:onchange="reloadPage()">
                <option 
                    th:each="yearRow : ${years}" 
                    th:value="${yearRow}" 
                    th:selected="(${yearRow} == ${year})"
                    th:text="${yearRow}">
                </option>
            </select>
            <!-- other HTML code here -->
        </main>

        <script th:inline="javascript" type="text/javascript">
            function reloadPage() {
                alert("You selected a year");
            }
        </script>
    </body>
</html>

The Layout fragment:

<!DOCTYPE html>
<html class="h-100" th:fragment="layout (title, css_assets, js_assets, content)" xmlns:th="http://www.thymeleaf.org">
<head>
    <!-- Usual head stuff - meta tags, links to Bootstrap CSS & fonts libraries etc -->
    <!-- Common CSS Styles -->
    <link rel="stylesheet" th:href="@{/css/styles.css}">
    
    <!-- Per-page placeholder for additional links -->
    <th:block th:replace="${css_assets}" />
        
    <title th:replace="${title}">Layout Title</title>
</head>
<body class="d-flex flex-column h-100">
    <header th:replace="~{fragments/header :: header}"></header>
   
    <div th:replace="${content}">
        <p>Layout content</p>
    </div>
       
    <footer th:replace="~{fragments/footer :: footer}"></footer>
    
    <!-- Common scripts -->
    <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js" integrity="sha384-MrcW6ZMFYlzcLA8Nl+NtUVF0sA7MsXsP1UyJoMp4YLEuNSfAP+JcXn/tWtIaxVXM" crossorigin="anonymous"></script>
    
    <!-- Per-page placeholder for additional scripts -->
    <th:block th:replace="${js_assets}" />
</body>
</html>

Inspecting the code, I don't see the inline script:

enter image description here

.. and in the console the Javascript function is not found: enter image description here

Of course, probably I can use the ${js_assets} to inject my JS function from an external JS file but it bothers me why is it not working with the inline syntax. Am I missing something?

I'm pretty new to Spring Boot and Thymeleaf so please be understanding. Thanks!


Solution

  • Thanks to andrewJames I realized that the only HTML code that gets injected from my view into the final HTML is the one included between the main tags - everything else that is not a fragment in the layout is replaced. The easiest fix would have been to include my inline script inside main.

    But since I want to have it right before the closing body tag I ended up leveraging the placeholder I already built into my layout for page-by-page JS.

    Here's the new code for view (notice that the parameter for js_assets sent in the layout is pointing now to the fragment in the page)

    <!doctype html>
    <html th:replace="~{fragments/layout :: layout(~{::title}, ~{}, ~{::js_assets}, ~{::main})}">
        <head>
            <title>Reports Page</title>
        </head>     
        <body>
            <main role="main" class="flex-shrink-0">
                <!-- some HTML code here -->
                <select class="form-select" id="year" aria-label="Year dropdown" th:onchange="reloadPage()">
                    <option 
                        th:each="yearRow : ${years}" 
                        th:value="${yearRow}" 
                        th:selected="(${yearRow} == ${year})"
                        th:text="${yearRow}">
                    </option>
                </select>
                <!-- other HTML code here -->
            </main>
    
            <th:block th:fragment="js_assets">
                <script th:inline="javascript" type="text/javascript">
                    function reloadPage() {
                        alert("You selected a year");
                    }
                </script>
            </th:block>
        </body>
    </html>