openplanning

Sử dụng Layout trong Thymeleaf

  1. Mục tiêu của bài học
  2. Ví dụ Thymeleaf Layout

1. Mục tiêu của bài học

Page Layout (bố cục trang) đề cập đến việc xắp xếp văn bản, hình ảnh và các đối tượng khác trên một trang, và nó là vấn đề được quan tâm lớn nhất của những người thiết kế website. Trong bài học này tôi sẽ giới thiệu kỹ thuật được sử dụng trong Thymeleaf để tạo ra một Layout.
Thymeleaf sử dụng các fragment để lắp ghép lại với nhau tạo thành một trang, vì vậy bạn nên học về fragment trước khi tiếp tục với bài học này.
Một website có thể có nhiều trang, nhưng các trang có một cấu trúc tương tự nhau. Chẳng hạn, dưới đây là một cấu trúc đơn giản:
Dựa trên cấu trúc trên bạn có thể tạo ra các trang khác nhau:

2. Ví dụ Thymeleaf Layout

Dưới đây là hình ảnh một ứng dụng, nó được viết trên Spring Boot, đừng lo lắng nếu bạn sử dụng một nền tảng (framework) khác, cấu trúc dự án có thể khác nhau một chút, nhưng không khó khăn cho bạn để hiểu các bước tôi đang thực hiện tại đây.
Tạo 2 tập tin main.css & main-layout.html:
Tập tin main-layout.html chứa 1 fragment có 6 tham số, các tham số này giúp định hình nên giao diện đầy đủ của một trang.
layouts/main-layout.html
<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org"
    th:fragment="main-fragment (title, otherStaticResources, header, nav, mainContent, footer)">
<head>
<meta charset="UTF-8" />
<title th:replace="${title}">Page 1 Title</title>
<link rel="stylesheet" type="text/css" th:href="@{/main.css}" href="../../static/main.css"/>
<!-- Other javascript, css source files -->
<th:block th:replace="${otherStaticResources} ?: ~{}"></th:block>
</head>
<body>
    <header th:insert="${header} ?: ~{}">
        <h2>Page 1</h2>
    </header>
    <section>
        <nav th:insert="${nav} ?: ~{}">
            <ul>
                <li><a href="#">Page 1</a></li>
                <li><a href="#">Page 2</a></li>
            </ul>
        </nav>
        <article th:insert="${mainContent} ?: ~{}">
            <h1>Page 1</h1>
            <p>Main content of Page 1</p>
        </article>
    </section>
    <footer th:insert="${footer} ?: ~{}">
        <p>Footer</p>
    </footer>
</body>
</html>
main.css
* {
    box-sizing: border-box;
}
header {
    background-color: #666;
    padding: 5px;
    text-align: center;
    font-size: 35px;
    color: white;
}
nav {
    float: left;
    width: 30%;
    height: 300px;
    background: #ccc;
    padding: 20px;
}
nav ul {
    list-style-type: none;
    padding: 0;
}
article {
    float: left;
    padding: 20px;
    width: 70%;
    background-color: #f1f1f1;
    height: 300px;
}
section:after {
    content: "";
    display: table;
    clear: both;
}
footer {
    background-color: #777;
    padding: 10px;
    text-align: center;
    color: white;
}
@media ( max-width : 600px) {
    nav, article {
        width: 100%;
        height: auto;
    }
}
Mở tập tin main-layout.html trực tiếp trên trình duyệt và bạn có thể nhìn thấy giao diện nó.
Tập tin app-fragments.html chứa các fragment được tái sử dụng trong nhiều Template khác nhau của ứng dụng.
fragments/app-fragments.html
<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8" />
<title>App Fragments</title>
</head>
<body>
    <!-- Default Navigator -->
    <ul th:fragment = "nav-default">
        <li><a th:href="@{/}">Home</a></li>
        <li><a th:href="@{/products}">Products</a></li>
        <li><a th:href="@{/about}">About</a></li>
    </ul>
    <!-- Admin Navigator -->
    <ul th:fragment = "nav-admin">
        <li><a th:href="@{/admin/products}">Product Management</a></li>
        <li><a th:href="@{/admin/orders}">Order Management</a></li>
    </ul>
    <div th:fragment="copyright">
      &copy; o7planning.org
    </div>
</body>
</html>
Tạo các trang khác cho ứng dụng của bạn:
  • home.html, products.html, about.html.
Tập tin home.html sử dụng Layout được định nghĩa bởi tập tin main-layout.html, và truyền (pass) các giá trị vào cho các tham số.
home.html
<!DOCTYPE html>
<!--  main-fragment (title, otherStaticResources, header, nav, mainContent, footer)  -->
<html xmlns:th="http://www.thymeleaf.org"
      th:replace="~{layouts/main-layout :: main-fragment(  
                                                ~{::title},
                                                ~{:: #home-static-resources},
                                                ~{:: #home-header},
                                                ~{:: #home-nav},
                                                ~{:: #home-main-content},  
                                                ~{:: #home-footer}
                                               )}">
                                              
<head>
    <title>Title of Home Page</title>
    <th:block id="home-static-resources">
        <script type="text/javascript" src="../static/home.js" th:src="@{/home.js}"></script>
        <link rel="stylesheet" type="text/css" href="../static/home.css" th:href="@{/home.css}"/>
    </th:block>
</head>
<body>
    <div id="home-header">
        Header of Home Page
    </div>
    <div id="home-nav" th:replace="~{/fragments/app-fragments :: nav-default}">
        Home Nav
    </div>
    <div id="home-main-content">
        <h2>Home content</h2>
        <div>Content of Home Page</div>
    </div>
    <div id="home-footer" th:replace="~{/fragments/app-fragments :: copyright}">
        Footer
    </div>
</body>
</html>
products.html
<!DOCTYPE html>
<!--  main-fragment (title, otherStaticResources, header, nav, mainContent, footer)  -->
<html xmlns:th="http://www.thymeleaf.org"
      th:replace="~{layouts/main-layout :: main-fragment(  
                                                ~{::title},
                                                ~{:: #products-static-resources},
                                                ~{:: #products-header},
                                                ~{:: #products-nav},
                                                ~{:: #products-main-content},  
                                                ~{:: #products-footer}
                                               )}">
                                              
<head>
    <title>Title of Products Page</title>
    <th:block id="products-static-resources">  
       <script type="text/javascript" src="../static/products.js" th:src="@{/products.js}"></script>
    </th:block>
</head>
<body>
    <div id="products-header">
        Header of Products Page
    </div>
    <div id="products-nav" th:replace="~{/fragments/app-fragments :: nav-default}">
         Nav
    </div>
    <div id="products-main-content">
        <h2>Product</h2>
        <p>Samsung</p>
        <p>iPhone</p>
        <p>Nokia</p>
    </div>
    <div id="products-footer" th:replace="~{/fragments/app-fragments :: copyright}">
        Footer
    </div>
</body>
</html>
about.html
<!DOCTYPE html>
<!--  main-fragment (title, otherStaticResources, header, nav, mainContent, footer)  -->
<html xmlns:th="http://www.thymeleaf.org"
      th:replace="~{layouts/main-layout :: main-fragment(  
                                                ~{::title},
                                                ~{:: #about-static-resources},
                                                ~{:: #about-header},
                                                ~{:: #about-nav},
                                                ~{:: #about-main-content},  
                                                ~{:: #about-footer}
                                               )}">
                                              
<head>
    <title>Title of About Page</title>
    <th:block id="about-static-resources">
        <script type="text/javascript" src="../static/about.js" th:src="@{/about.js}"></script>
    </th:block>
</head>
<body>
    <div id="about-header">
        Header of About Page
    </div>
    <div id="about-nav" th:replace="~{/fragments/app-fragments :: nav-default}">
         Nav
    </div>
    <div id="about-main-content">
        <h2>About</h2>
        <div>Content of About Page</div>
    </div>
    <div id="about-footer" th:replace="~{/fragments/app-fragments :: copyright}">
        Footer
    </div>
</body>
</html>
MainController.java
package org.o7planning.thymeleaf.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
public class MainController {
    @RequestMapping("/")
    public String home() {
        return "home";
    }
    @RequestMapping("/products")
    public String products() {
        return "products";
    }
    @RequestMapping("/about")
    public String about() {
        return "about";
    }
}