Search code examples
spring-boottemplatescomponentsthymeleafhtml-parsing

thymeleaf parsing template issues, the path fails to get recognized



  1. I have 2 Root Cause Issues/Errors
org.attoparser.ParseException: Error resolving template [components/folder-list], template might not exist or might not be accessible by any of the configured Template Resolvers (template: "inbox-page" - line 65, col 14)
org.thymeleaf.exceptions.TemplateInputException: Error resolving template [components/folder-list], template might not exist or might not be accessible by any of the configured Template Resolvers (template: "inbox-page" - line 65, col 14) 
  • The bug arised when I converted some .html code and attempted to extract it as a component.
  • I tried different paths from absolute path to package path; path fails to get recognized regardless.

templates/inbox-page.html

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <title>Inbox</title>
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <link
      href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.0/dist/css/bootstrap.min.css"
      rel="stylesheet"
      integrity="sha384-KyZXEAg3QhqLMpG8r+8fhAXLRk2vvoC2f3B09zVXn8CA5QIVfZOJ3BCsw2P0p/We"
      crossorigin="anonymous"
    />
    <script
      src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.0/dist/js/bootstrap.bundle.min.js"
      integrity="sha384-U1DAWAznBHeqEIlVSCgzq+c9gqGAJn5c/t99JyeKa9xxaYpSvHU5awsuZVVFIhvj"
      crossorigin="anonymous"
    ></script>
    <style>
      .container {
        display: grid;

        grid-template-areas:
          "header header header"
          "nav content content"
          "footer footer footer";

        grid-template-columns: 300px 1fr;
        grid-template-rows: auto 1fr auto;
        grid-gap: 10px;

        height: 100vh;
      }
      header {
        grid-area: header;
      }

      nav {
        grid-area: nav;
        margin-left: 0.5rem;
      }

      main {
        grid-area: content;
      }

      footer {
        grid-area: footer;
      }
    </style>
  </head>
  <body>
    <div class="container">
      <header>
        <h1>Welcome, Name</h1>
      </header>







      <nav>
        <div th:insert="components/folder-list :: folder-list (panelName = 'Folders', folders = ${defaultFolders})" ></div>
        <div th:insert="components/folder-list :: folder-list (panelName = 'My folders', folders = ${userFolders})"></div>
      </nav>
      
      <main> 
        <p> Email LIST</p>
      </main>

      <footer>
        <!-- Footer content -->
      </footer>
    </div>
  </body>
</html>

templates/index.html

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <title>Demo</title>
    <meta name="description" content="" />
    <meta name="viewport" content="width=device-width" />
    <base href="/" />
  </head>
  <body>
    <h1>Login</h1>

    <div>
      With GitHub: <a href="/oauth2/authorization/github">click here</a>
    </div>
  </body>
</html>

components/folder-list.html

<!DOCTYPE html>

<html >
  <head>
  </head>
  

  <body>
    <div th:fragment="folder-list (panelName, folders)">
      <div class="card" th:if="${folders}">
        <div class="card-header" th:text="${panelName}">Folders</div>
        <div class="card-body">
          <div class="list-group">
            <li
              th:each="folder :: ${folders}"
              class="list-group-item d-flex justify-content-between align-items-center"
            >
              <span th:text="${folder.label}">Folder</span>
              <span class="badge bg-primary rounded-pill">15</span>
          </li>
          </div>
        </div>
  </body>
</html>

InboxApp.java DriverCode

package io.aharo.inbox;

import io.aharo.inbox.folders.Folder;
import io.aharo.inbox.folders.FolderRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.cassandra.CqlSessionBuilderCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.PostConstruct;
import java.nio.file.Path;

@SpringBootApplication
@RestController
public class InboxApp
{
    @Autowired 
    private FolderRepository folderRepository;

    public static void main(String[] args) {
        SpringApplication.run(InboxApp.class, args);

        // System.out.println(folderRepository);
        // FolderRepository fr = new FolderRepository("aharo", "idk", "red");
    }

    /**
     * This is neccesary for the Spring Boot App to use the Astra secure bundle
     * && connect to our database.
     */
    @Bean
    public CqlSessionBuilderCustomizer sessionBuilderCustomizer (DataStaxAstraProperties astraProperties)
    {
        Path bundle = astraProperties.getSecureConnectBundle().toPath();
        return builder -> builder.withCloudSecureConnectBundle(bundle);
    }

    @PostConstruct
    public void init()
    {
        folderRepository.save( new Folder("aharo","Inbox", "blue"));
        folderRepository.save( new Folder("aharo","Sent", "green"));
        folderRepository.save( new Folder("aharo","Important", "yellow "));

    }

    // @RequestMapping("/user")
    // public String user(@AuthenticationPrincipal OAuth2User principal) {
    //  System.out.println(principal);
    //  return principal.getAttribute("name");
    // }
    

}

Environment Tree

  • I am unable to show pictures but my projects work tree environment looks like this.
.
├── mvnw
├── mvnw.cmd
├── pom.xml
├── README.md
├── src
│  └── main
│     ├── java
│     │  └── io
│     │     └── aharo
│     │        └── inbox
│     │           ├── controllers
│     │           │  └── InboxController.java
│     │           ├── DataStaxAstraProperties.java
│     │           ├── folders
│     │           │  ├── Folder.java
│     │           │  ├── FolderRepository.java
│     │           │  └── FolderService.java
│     │           ├── InboxApp.java
│     │           └── SecurityAdapter.java
│     └── resources
│        ├── application.yml
│        ├── components
│        │  └── folder-list.html
│        ├── META-INF
│        │  └── additional-spring-configuration-metadata.json
│        ├── secure-connect.zip
│        └── templates
│           ├── inbox-page.html
│           └── index.html
└── target
   ├── classes
   │  ├── application.yml
   │  ├── components
   │  │  └── folder-list.html
   │  ├── io
   │  │  └── aharo
   │  │     └── inbox
   │  │        ├── controllers
   │  │        │  └── InboxController.class
   │  │        ├── DataStaxAstraProperties.class
   │  │        ├── folders
   │  │        │  ├── Folder.class
   │  │        │  ├── FolderRepository.class
   │  │        │  └── FolderService.class
   │  │        ├── InboxApp.class
   │  │        └── SecurityAdapter.class
   │  ├── META-INF
   │  │  └── additional-spring-configuration-metadata.json
   │  ├── secure-connect.zip
   │  └── templates
   │     ├── inbox-page.html
   │     └── index.html
   ├── generated-sources
   │  └── annotations
   ├── maven-status
   │  └── maven-compiler-plugin
   │     └── compile
   │        └── default-compile

OLD CODE

  • templates/inbox-page.html old but works.
  • If we swap the new inbox-page.html with this old code, it will work but what I want is to convert it as a component.
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <title>Inbox</title>
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <link
      href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.0/dist/css/bootstrap.min.css"
      rel="stylesheet"
      integrity="sha384-KyZXEAg3QhqLMpG8r+8fhAXLRk2vvoC2f3B09zVXn8CA5QIVfZOJ3BCsw2P0p/We"
      crossorigin="anonymous"
    />
    <script
      src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.0/dist/js/bootstrap.bundle.min.js"
      integrity="sha384-U1DAWAznBHeqEIlVSCgzq+c9gqGAJn5c/t99JyeKa9xxaYpSvHU5awsuZVVFIhvj"
      crossorigin="anonymous"
    ></script>
    <style>
      .container {
        display: grid;

        grid-template-areas:
          "header header header"
          "nav content content"
          "footer footer footer";

        grid-template-columns: 300px 1fr;
        grid-template-rows: auto 1fr auto;
        grid-gap: 10px;

        height: 100vh;
      }
      header {
        grid-area: header;
      }

      nav {
        grid-area: nav;
        margin-left: 0.5rem;
      }

      main {
        grid-area: content;
      }

      footer {
        grid-area: footer;
      }
    </style>
  </head>
  <body>
    <div class="container">
      <header>
        <h1>Welcome, Name</h1>
      </header>






  <nav>
    <div class="card">
          <div class="card-header">defaultFolders</div>
          <div class="card-body">
            <ul class="list-group">
              <li
                th:each="folder : ${defaultFolders}"
                class="list-group-item d-flex justify-content-between align-items-center"
              >
                TMP
                <span th:text="${folder.label}"> Label</span>
                <span class="badge bg-primary rounded-pill">69</span>
              </li>
            </ul>
    </div>

        <div class="card"
             th:if="${userFolders}">
              <div class="card-header">userFolders</div>
              <div class="card-body">
                <ul class="list-group">
                  <li
                    th:each="folder : ${defaultFolders}"
                    class="list-group-item d-flex justify-content-between align-items-center"
                  >
                    TMP
                    <span th:text="${folder.label}"> Label</span>
                    <span class="badge bg-primary rounded-pill">24</span>
                  </li>
                </ul>    
    </div>
  </nav>



      <main>
        <div class="card">
          <div class="card-header">Inbox</div>
          <div class="card-body">
            <div class="list-group">
              <a
                href="#"
                class="list-group-item list-group-item-action active"
                aria-current="true"
              >
                <div class="d-flex w-100 justify-content-between">
                  <h5 class="mb-1">LIST GROUP</h5>
                  <small></small>
                </div>
                <p class="mb-1"></p>
                <small></small>
              </a>
              <a href="#" class="list-group-item list-group-item-action">
                <div class="d-flex w-100 justify-content-between">
                  <h5 class="mb-1">LIST GROUP</h5>
                  <small class="text-muted"></small>
                </div>
                <p class="mb-1"></p>
                <small class="text-muted"></small>
              </a>
              <a href="#" class="list-group-item list-group-item-action">
                <div class="d-flex w-100 justify-content-between">
                  <h5 class="mb-1">LIST GROUP</h5>
                  <small class="text-muted"></small>
                </div>
                <p class="mb-1"></p>
                <small class="text-muted"></small>
              </a>
            </div>
          </div>
        </div>
      </main>

      <footer>
        <!-- Footer content -->
      </footer>
    </div>
  </body>
</html>

Solution

  • Move your components folder inside of templates. Thymeleaf searches everything relative to templates.