Mục tiêu

  • Cài đặt blog controller.
  • Cài đặt mẫu chi tiết bài blog.
  • Hiển thị trang chi tiết bài blog.

1. Tạo Controller cho trang chi tiết

Bạn có thể tạo một lớp controller có tên WebPersonalBlogController và thừa kế BlogController:

@Controller
public class WebPersonalBlogController extends BlogController {}

Vì bên trong lớp BlogController đã có sẵn API /blog/{slug} cho trang chi tiết blog:

@DoGet("/blog/{slug}")
public View blogSlugGet(
    HttpServletRequest request,
    @PathVariable String slug
) {
    String languageCode = languageControllerService
        .getLanguageCodeOrDefault(request);
    WebPostDetailsResponse post = postControllerService
        .getPublishedPostByTypeAndSlug(
            PostType.BLOG.toString(),
            slug,
            languageCode
        );
    long postId = post.getId();
    String pageImageUrl = getMediaUrlOrNull(post.getFeaturedImage());
    View.Builder viewBuilder = View.builder()
        .template("blog/details")
        .addVariable("blog", post)
        .addVariable("pageTitle", post.getTitle())
        .addVariable("pageImageUrl", pageImageUrl)
        .addVariable(
            "pageFragments",
            pageFragmentManager.getPageFragmentMap(
                "blog_details",
                languageCode
            )
        )
        .putKeyValueToVariable(
            "additionalValueMap",
            "ratingItemType",
            TABLE_NAME_POST
        )
        .putKeyValueToVariable(
            "additionalValueMap",
            "ratingItemId",
            postId
        );
    decorateViewPageDescription(viewBuilder, post.getSummary());
    decorateViewAdditionSections(viewBuilder, post);
    decorateBlogDetailsView(request, postId, viewBuilder);
    return viewBuilder.build();
}

Nó đã chỉ định sẵn template blog/details, vậy nên chúng ta chỉ cần cài đặt template này mà không cần làm gì thêm.


2. Cài đặt template blog/details

Trong ta sẽ tạo tập tin blog/details.html dưới cấu trúc thư mục như sau:

src/main/resources/
└── templates/
    └── blog/
        └── detail.html

Và chúng ta có thể cung cấp nội dung cho nó như sau:

<!DOCTYPE html>
<html
    xmlns="http://www.w3.org/1999/xhtml"
    xmlns:th="http://www.thymeleaf.org"
    xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
    layout:decorate="~{ezytheme}">
<body>
<div layout:fragment="content" class="page-wrapper">
  <div class="container">
    <section class="page-header">
        <div th:replace="~{fragments/breadcrumb :: content(title=${blog.title}, backText=#{back_to_home}, backUrl='/')}"></div>
    </section>
    <section class="page-content page-article">
      <div class="row">
        <div class="col-12">
          <span>[[#{posted_by}]]: [[${blog.author.name}]]</span>
          <span>
            [[${#strings.toLowerCase(#messages.msg('at'))}]]: <span class="date-time-minute-string">[[${blog.publishedAt}]]</span>
          </span>
        </div>
        <div th:if="${blog.terms.size() > 0}" class="col-12">
          <div class="terms-wrapper">
            <span>[[#{in}]]:</span>
            <div class="terms">
              <a th:each="term : ${blog.terms}">
                [[${term.name}]]
              </a>
            </div>
          </div>
        </div>
        <div class="col-lg-2"></div>
        <div class="col-lg-8">
          <div class="row">
            <div th:if="${blog.featuredImage != null}">
              <img th:src="${blog.featuredImage.getUrlOrNull()}">
            </div>
            <div class="col-md-12 article-content" th:utext="${blog.content}"></div>
          </div>
        </div>
        <div class="col-lg-2"></div>
      </div>
    </section>
  </div>
</div>
</body>
</html>

Để nội dung hiển thị được đẹp, chúng ta có thể bổ sung css vào tập tin main.css, ví dụ:

.breadcrumb {
    background-color: rgba(0, 0, 0, 0);
    padding: 0;
    margin: 0;
}

.page-header {
    padding-top: 45px;
    padding-bottom: 30px;
}

.page-header .breadcrumb {
    display: flex;
    flex-direction: column;
    align-items: center;
}

.page-header .breadcrumb .title {
    font-size: 32px;
    text-transform: uppercase;
    text-align: center;
}

.page-header .breadcrumb .link {
    text-align: center;
    padding-top: 20px;
}

.page-article img {
    max-width: 100%;
    margin-top: 12px;
    margin-bottom: 12px;
}

.terms-wrapper {
    display: flex;
    align-items: center;
    gap: 10px;
}

.terms-wrapper .terms {
    display: flex;
    align-items: center;
    gap: 10px;
}

Sau đó chúng ta trở lại trình duyệt, refresh lại chúng ta có thể nhận được kết quả như sau:

Screenshot 2025-10-11 at 16.32.50.png

Tài liệu liên quan