Nôi dung của phần này bao gồm:
- Viết mã hiển thị danh sách các bài viết trong mục bài viết.
- Xây dựng chức năng tìm kiếm cho Blog.
- Xây dựng chức năng lọc bài viết theo danh mục
Viết mã hiển thị danh sách các bài viết trong mục bài viết
Ý tưởng: trang bài viết trong Blog là một trang dùng để hiển thị tất cả các bài viết có trong Blog. Chúng ta lấy ra danh sách các bài viết theo thứ tự bài viết mới nhất trước.
Người dùng nhấn vào mục Bài viết trong phần Header:
Hiển thị danh sách các bài viết:
Thứ tự thực hiện:
Bước 1: Viết một controller mới xử lý cho các hành động trong trang Bài viết. Ví dụ: controller này được đặt tên là: WebEssentialListPostController và khi một yêu cầu lấy dữ liệu được gửi đến qua đường dẫn /post-list nó sẽ thực hiện phương thức listPost() được viết trong controller này.
Giống như ở phần trước, các tham số (param) được sử dụng để lấy ra bài viết theo một số yêu cầu của người dùng. Trong đó:
- nextPageToken, prevPageToken, lastPage, limit là các tham số được sử dụng để phân trang bài viết.
- authorUuid là tham số sử dụng để lấy ra bài viết theo tác giả.
- termSlug là tham số để lấy ra bài viết theo slug.
- keyword là tham số để lấy ra bài viết với từ khóa nhận được (tham số này sẽ được sử dụng cho chức năng tìm kiếm bài viết).
- categoryIds là tham số để lấy ra các bài viết theo danh mục (tham số này sẽ được sử dụng cho chức năng lọc bài viết theo danh mục).
Bên trên là các đoạn mã được sử dụng để lấy ra danh sách bài viết:
- Đoạn mã 1: được sử dụng để validate các param.
- Đoạn mã 2: được sử dụng để lấy ra các bài viết theo thứ tự mặc định (Ngày giảm dần).
- Đoạn mã 3: được sử dụng để ra tên và slug của danh mục với type là category (được sử dụng trong phần xây dựng chức năng lọc bài viết theo danh mục).
- Đoạn mã 4: trả về kết quả là một View với các tham số: danh sách bài viết, danh sách danh mục, từ khóa tìm kiếm.
Kết quả trả về là một danh sách các bài viết được lưu trữ trong listPostPagination.items
Câu lệnh điều kiện th:if kiểm tra xem có tồn tại mọt bài viết nào không, nếu có nó hiển thị ra các flagments đã được định nghĩa cho mỗi bài viết.
Dưới đây là đoạn mã cho card_whiteboard:
<div th:fragment="card_whiteboard" class="card--medium card-whiteboard"> <a th:href="${'/posts/' + latestPost.slug}"> <img th:src="${latestPost.image.getUrlOrNull()}" alt=""/> </a> <div class="card-info"> <div class="card-avatar"> <a href="#" ><img src="https://secure.gravatar.com/avatar/82d76ac5aea6be5ad3feceb3a8547b41?s=60&d=mm&r=g" alt="" /> </a> </div> <div class="container-for-avatar"> <div class="card-author"> <i>[[#{by}]] </i><a href="#"><b>[[${latestPost.author.name}]]</b></a> </div> <div class="card-date date-string">[[${latestPost.publishedAt}]]</div> </div> </div> <div class="card-title"><a th:href="${'/posts/' + latestPost.slug}">[[${latestPost.title}]]</a></div> <div class="card-content"> [[${latestPost.content.replaceAll("<[^>]*>", "")}]] </div> <ul class="card-index"> <li> <i class="far fa-comment-alt"></i> <span class="commas-number-string">[[${latestPost.getCommentCount()}]]</span> </li> <li> <i class="far fa-clock"></i> <span>2 min</span> </li> <li> <i class="fas fa-signal"></i> <span class="commas-number-string">[[${latestPost.getViewCount()}]]</span> </li> </ul>
</div>
Các nút bấm chuyển trang, để các nút này được hiển thị nó phải kiểm tra trong listPostPagination xem có tồn tại trang kế tiếp hay trang trước đó hay không:
Các nút bấm trên có chứa hàm (fetchPostList()) được sử dụng để lấy ra các bài viết cho trang trước hoặc trang sau. Trong hàm trên có sử dụng ajax để lấy ra dữ liệu của các bài viết ở trang tiếp theo hoặc trang trước đó. Sau đó, thay thế một đoạn mã html cùng với dữ liệu lấy được vào trong đoạn mã html hiện có, giúp hiển thị các bài viết mới. Dưới đây là đoạn mã sử dụng:
- Đoạn 1: xác định hành động nhấn phím của người dùng và chuyển hướng tới trang tương ứng.
- Đoạn 2: xác định ngôn ngữ.
- Đoạn 3: sử dụng ajax để xử lý yêu cầu GET với các param tương ứng tới url.
- Đoạn 4: Nếu nhận được dữ liệu thì gán lại pageToken, thay thế các bài viết mới, kiểm tra xem có trang trước hoặc trang sau hay không.
- Đoạn 5: Nếu lỗi thì gọi hàm và đưa ra lỗi.
Trong đoạn mã trên, chúng ta gửi yêu cầu để lấy các bài viết mới tới một API của dự án.
Đoạn mã trên có chức năng gần giống với đoạn mã được viết trong WebEssentialListPostController. Đoạn mã trong WebEssentialListPostController trả về một View chứa thông tin về các bài viết và một số thông tin khác. Còn trong phương thức xử lý này, nó chỉ trả về các bài viết - PaginationModel. Dưới đây là toàn bộ mã có trong phương thức:
Sau khi đã lấy ra được dữ liệu (data), các data.items được sử dụng để tạo các đoạn mã html mới (các bài viết mới):
Xây dựng chức năng tìm kiếm cho Blog
Như trong phần trước tại controller WebEssentialListPostController, chúng ta có sử dụng một param tên là keyword. Param này được sử dụng để tìm kiếm các bài viết theo từ khóa (sử dụng tìm kiếm gần bằng). Để sử dụng chúng ta chỉ cần truyền một param chứa từ khóa tìm kiếm đến url. Dưới đây là mã javascript xử lý cho việc lấy từ khóa tìm kiếm và gửi đi:
Đoạn mã trên lấy giá trị (keyword) trong ô input và gửi kèm trong url (?keyword). Sau khi gửi, controller nhận được yêu cầu, nó sẽ xử lý keyword này và lấy ra các bài viết có chứa keyword đó. Cách hoạt động tương tự với phần lấy ra bài viết trước đó, nhưng trong lần xử lý này chúng ta chỉ thêm vào một điều kiện để lọc ra các bài viết được lấy.
Xây dựng chức năng lọc bài viết theo danh mục
Ý tưởng:
- Tại trang bài viết chứa danh sách bài viết, khi chưa chọn danh mục nào trong bộ lọc thì tất cả các bài viết được hiển thị. Khi chọn các danh mục trong bộ lọc thì tất cả các bài viết của danh mục đã chọn sẽ được lấy ra.
- Khi tìm kiếm một bài viết theo từ khóa, chúng ta có thể sử dụng bộ lọc danh mục để đưa ra kết quả tìm kiếm theo các danh mục.
Các bước thực hiện:
Bước 1: Gán giá trị cho các danh mục trong bộ lọc danh mục.
Bước 2: Mỗi khi check và uncheck cho một mục trong danh mục, sử dụng một hàm để cập nhật lại dữ liệu (sử dụng ajax trong hàm này để lấy dữ liệu cho các bài viết trong các danh mục được chọn).
Bộ lọc sẽ có giao diện như sau:
Bộ lọc theo danh mục trên có: tên là tên của term, có giá trị là id của term và đều thuộc term type là CATEGORY. Khi trang bài viết được khởi tạo, đi cùng với view sẽ chứa một biến lưu trữ tên và id của các danh mục, như sau:
Mã html cho bộ lọc danh mục như sau:
Sử dụng hàm updatePostFilterByCate() để lấy ra các ô checkbox được check, lấy value của các ô checkbox đó (value ở đây là các id). Sau đó lấy ra danh sách các bài viết theo id.
Trong hàm trên, pageToken{} được sử dụng để reset lại lại trang (các kết quả tìm kiếm trở về trang đầu tiên).
Đoạn mã trên kiểm tra xem có bất id nào được truyền vào hay không, nếu có thì thêm vào queryString và nếu có nhiều giá trị thì chúng ngăn cách nhau bởi dấu
,
. Lúc này, trong các param mà controller nhận được đã xuất hiện một danh sách các termId. Do đó, chúng ta cần nhận và xử lý các ids này để đưa ra các bài viết phù hợp.
Bổ sung các tham số categoryIds cho hàm getPostPagination() (Sử dụng overload để viết một hàm tương tự nhưng thêm tham số categoryIds vào):
Trong postFilterFactory thêm termIds để lọc ra các bài viết theo danh sách ids tương ứng. Do chức năng tìm kiếm và lọc bài viết đều sử dụng chung một hàm fetchPostList() do đó các keyword tìm kiếm và các danh mục tìm kiếm đều được thêm vào queryString trước khi nó được gửi đi. Do đó, chúng ta có thể lọc ra danh mục các bài viết mà chúng ta đang tìm kiếm.