Wednesday, 26 October 2016

How to integrate Thymeleaf and Bootstrap in a Spring boot application ?



  • Install eclipse Kepler
  • Create new Maven project
    • Check Create a simple project
    • Provide options : 
      • Group Id = com.company
      • Artifact Id = example1
      • Packaging = war
    • Click create
    • It will create structure like :
      • # src/main/java 
      • # src/main/resources 
      • # src/test/java 
      • # src/test/resources 
      • > src/main/webapp 
      • > JRE system lib (Default JRE set in eclipse) 
      • > pom.xml
  • Provide Spring boot dependencies :
    <project ...>
       ...
       <parent>
         <groupId>org.springframework.boot</groupId>
         <artifactId>spring-boot-starter-parent</artifactId>
         <version>1.4.1.RELEASE</version>
       </parent>


       <properties>
         <main.basedir>${basedir}/../..</main.basedir>
         <thymeleaf.version>3.0.2.RELEASE</thymeleaf.version>
         <thymeleaf-layout-dialect.version>

              2.0.4
         </thymeleaf-layout-dialect.version>
       </properties>




       <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>

       <dependencies>
  </project>
    •  It will add all the Spring boot dependencies under Maven dependencies 

  • Put static contents of Bootstrap CSS and jQuery APIs :
    # src/main/resources/static/css/bootstap.min.css
    # src/main/resources/static/js/jquery-1.7.2.js
    # src/main/resources/static/js/jquery.validate.js# src/main/resources/static/favicon.ico
  • Create application.properties under resources and add configuration :
    # Allow Thymeleaf templates to be reloaded at dev time
    spring.thymeleaf.cache: false
    spring.thymeleaf.mode: html

  • Create some java classes for data model and manipulate data
         1. Message.java
             public class Message {
         private Long id;


         @NotEmpty(message = "Message is required.")
         private String text;

         @NotEmpty(message = "Summary is required.")
         private String summary;

         private Calendar created = Calendar.getInstance();

  
         // getters and setters
      }

          2.  MessageRepository.java
      public interface MessageRepository {
         Iterable<Message> findAll();
         Message save(Message message);
         Message findMessage(Long id);
         void deleteMessage(Long id);
      }


         3InMemoryMessageRepository.java
       public class InMemoryMessageRepository 
                          implements MessageRepository {
         private static AtomicLong counter = new AtomicLong();

         private final ConcurrentMap<Long, Message> messages = 

                         new ConcurrentHashMap<Long, Message>();
 

         public Iterable<Message> findAll() {
             return this.messages.values();
         }

         public Message save(Message message) {
             Long id = message.getId();
             if (id == null) {
                id = counter.incrementAndGet();
                message.setId(id);
             }
             this.messages.put(id, message);
             return message;
         }

         public Message findMessage(Long id) {
             return this.messages.get(id);
         }

         public void deleteMessage(Long id) {
            this.messages.remove(id);
         }
     }

 
  • Create master Application with main(), bean to access data and Converter bean
    @SpringBootApplication
    public class Application {

       @Bean
       public MessageRepository messageRepository() {
          return new InMemoryMessageRepository();
       }

       @Bean
       public Converter<String, Message> messageConverter() {
          return new Converter<String, Message>() {
              public Message convert(String id) {
                   return messageRepository().

                                findMessage(Long.valueOf(id));
              }
          };
       }


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


  • Create the Controller to handle different application path
    @Controller
    @RequestMapping ("/")

    public class MessageController {
     private final MessageRepository messageRepository;

     public MessageController(MessageRepository messageRepository){
         this.messageRepository = messageRepository;
     }

     @GetMapping
     public ModelAndView list() {
         Iterable<Message> messages=this.messageRepository.findAll();
         return new ModelAndView("messages/list", "messages", messages);
     }

     @GetMapping("{id}")
     public ModelAndView view(@PathVariable("id") Message message){
         return new ModelAndView("messages/view", "message", message);
     }

     @GetMapping(params = "form")
     public String createForm(@ModelAttribute Message message) {
         return "messages/form";
     }

     @PostMapping
     public ModelAndView create(@Valid Message message,
             BindingResult result, RedirectAttributes redirect) {
         if (result.hasErrors()) {
            return new ModelAndView("messages/form", "formErrors",
                            result.getAllErrors());
         }

         message = this.messageRepository.save(message);
         redirect.addFlashAttribute("globalMessage",
                           "Successfully created a new message");
         return new ModelAndView("redirect:/{message.id}",
                           "message.id", message.getId());
     }

     @RequestMapping("foo")
     public String foo() {
         throw new RuntimeException("Expected exception in controller");
     }

     @GetMapping(value = "delete/{id}")
     public ModelAndView delete(@PathVariable("id") Long id) {
         this.messageRepository.deleteMessage(id);
         Iterable<Message> messages = this.messageRepository.findAll();
         return new ModelAndView("messages/list", "messages", messages);
     }

     
     @GetMapping(value = "modify/{id}")
     public ModelAndView modifyForm(@PathVariable("id") Message message) {
         return new ModelAndView("messages/form", "message", message);
     }

     @RequestMapping (path="/name", method=RequestMethod.GET)
        public String getName() {
            return "Hello World";
        }
     }

  • Create pages :
    > src/main/resources/templates/messages/form.html
    > src/main/resources/templates/messages/list.html
    > src/main/resources/templates/messages/view.html
    > src/main/resources/templates/layout.html

layout.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org"
    xmlns:layout="http://www.ultraq.net.nz/web/thymeleaf/layout">
<head>
<title>Layout</title>
<link rel="stylesheet" th:href="@{/css/bootstrap.min.css}"
    href="../../css/bootstrap.min.css" />
</head>
<body>
  <div class="container">
    <div class="navbar">
       <div class="navbar-inner">
        <a class="brand"
          href="https://github.com/ultraq/thymeleaf-layout-dialect">
            Thymeleaf - Layout
        </a>

        <ul class="nav">
        <li>
          <a th:href="@{/}" href="messages.html"> Messages </a>
        </li>
        </ul>
       </div>
     </div>

     <h1 layout:fragment="header">Layout</h1>
 
   <div layout:fragment="content">Fake content</div>
  </div>
</body>
</html>


list.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org"
    xmlns:layout="http://www.ultraq.net.nz/web/thymeleaf/layout"
    layout:decorate="layout">
<head>
<title>Messages : View all</title>
</head>

<body>
  <h1 layout:fragment="header">Messages : View all</h1>
  <div layout:fragment="content" class="container">
     <div class="pull-right">
        <a href="form.html" th:href="@{/(form)}">Create Message</a>
     </div>
     <table class="table table-bordered table-striped">
       <thead>
         <tr>
           <td>ID</td>
          <td>Created</td>
          <td>Summary</td>
         </tr>
       </thead>
       <tbody>
         <tr th:if="${messages.empty}">
           <td colspan="3">No messages</td>
         </tr>
         <tr th:each="message : ${messages}">
           <td th:text="${message.id}">1</td>
           <td th:text="${#calendars.format(message.created)}">
             July 11,2012 2:17:16 PM CDT
           </td>
           <td><a href="view.html" 
              th:href="@{'/' + ${message.id}}"
              th:text="${message.summary}"
                The summary
             </a>
           </td>
         </tr>
       </tbody>
       </table>
    </div>
 </body>
</html>

view.html
<html xmlns:th="http://www.thymeleaf.org"
    xmlns:layout="http://www.ultraq.net.nz/web/thymeleaf/layout"
    layout:decorate="layout">
<head>
<title>Messages : View</title>
</head>
<body>
  <h1 layout:fragment="header">Messages : Create</h1>
  <div layout:fragment="content" class="container">
     <div class="alert alert-success" th:if="${globalMessage}"
            th:text="${globalMessage}">Some Success message</div>
        
     <div class="pull-right">
        <a th:href="@{/}" href="list.html"> Messages </a>
     </div>
     <dl>
       <dt>ID</dt>
       <dd id="id" th:text="${message.id}">123</dd>
       <dt>Date</dt>
       <dd id="created"  
           th:text="${#calendars.format(message.created)}">
                July 11, 2012 2:17:16 PM CDT
       </dd>
       <dt>Summary</dt>
       <dd id="summary" th:text="${message.summary}">
          A short summary...
       </dd>
       <dt>Message</dt>
       <dd id="text" th:text="${message.text}">
          A detailed message that is longer than the summary.
       </dd>
      </dl>
    
     <div class="pull-left">
       <a href="messages" th:href="@{'/delete/' + ${message.id}}">
           delete 
       </a> | 
       <a href="form.html" th:href="@{'/modify/' + ${message.id}}"
           modify 
       </a>
     </div>
   </div>
 </body>
</html>

form.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org"
    xmlns:layout="http://www.ultraq.net.nz/web/thymeleaf/layout"
    layout:decorate="layout">
<head>
<title>Messages : View all</title>
</head>
<body>
  <h1 layout:fragment="header">Messages : View all</h1>
  <div layout:fragment="content" class="container">
      <div class="pull-right">
        <a href="form.html" th:href="@{/(form)}">Create Message</a>
      </div>

      <table class="table table-bordered table-striped">
        <thead>
         <tr>
            <td>ID</td>
           <td>Created</td>
           <td>Summary</td>
         </tr>
         </thead>
         <tbody>
           <tr th:if="${messages.empty}">
               <td colspan="3">No messages</td>
           </tr>
           <tr th:each="message : ${messages}">
               <td th:text="${message.id}">1</td>
               <td th:text="${#calendars.format(message.created)}">
                  July 11, 2012 2:17:16 PM CDT
               </td>
               <td>
                <a href="view.html" th:href="@{'/' + ${message.id}}"
                 th:text="${message.summary}"> The summary </a>
               </td>
           </tr>
        </tbody>
      </table>
    </div>
 </body>
</html>

  • Run the application by right click on Application.java and choose "Run as Java application" as a Java application
    After embedded tomcat start, you can access resources using request path and controller :
    http://localhost:8080/
         

No comments:

Post a Comment

Note: only a member of this blog may post a comment.