0
0
Lập trình
Hưng Nguyễn Xuân 1
Hưng Nguyễn Xuân 1xuanhungptithcm

Hướng Dẫn Phân Trang Danh Sách Bài Viết Trong Backend Blog - Phần 4

Đăng vào 3 tuần trước

• 7 phút đọc

Chủ đề:

@ezyplatform

Hướng Dẫn Phân Trang Danh Sách Bài Viết

Giới Thiệu

Trong phần này, chúng ta sẽ tìm hiểu cách thực hiện phân trang cho danh sách các bài viết trong ứng dụng Blog của mình, dựa trên ngày xuất bản. Phân trang không chỉ giúp tăng trải nghiệm người dùng mà còn cải thiện hiệu suất thao tác với dữ liệu lớn.

Ý Tưởng Thực Hiện

Chúng ta sẽ thiết kế một nút bấm (load more) trên giao diện. Khi nhấn vào nút này, ứng dụng sẽ tự động tải thêm các bài viết (nếu có) mà không cần tải lại toàn bộ trang.

Cấu Hình Phía Client

Để bắt đầu, chúng ta cần thiết lập một nút bấm và xử lý sự kiện cho nút này.

Hàm fetchPostList()

Hàm fetchPostList() sẽ gửi yêu cầu đến API: /api/v1/posts để lấy danh sách bài viết. Nếu có nextPageToken, nó sẽ được thêm vào queryString để yêu cầu trang tiếp theo. Nếu ezyweb.lang tồn tại, nó cũng sẽ được thêm vào để đảm bảo tính đa ngôn ngữ. Sau đó, chúng ta sử dụng ajax để gửi yêu cầu với các tham số đã xác định.

Chúng ta sẽ hạn chế số lượng bài viết được lấy ra trong mỗi lần nhấn nhờ tham số limit=3.

Nếu yêu cầu thành công (.done()), dữ liệu trả về sẽ cập nhật lastPostPageToken, thêm các bài viết mới vào #postListBody và kiểm tra xem còn bài viết nào không. Nếu không còn bài viết, chúng ta sẽ xóa nút next. Trong trường hợp yêu cầu thất bại (.fail()), ta sẽ gọi hàm ezyweb.processGetApiErrors(e) để xử lý các lỗi phát sinh.

Hàm buildPostListBodyHtml(posts)

Hàm này sẽ được sử dụng để xây dựng HTML cho danh sách các bài viết từ data.items.

Thiết Lập Phía Backend

Xử Lý RequestParam

Chúng ta sẽ sử dụng RequestParam để ánh xạ các tham số từ yêu cầu HTTP đến các tham số của phương thức trong controller. Điều này cho phép chúng ta dễ dàng trích xuất và sử dụng các tham số trong logic xử lý yêu cầu.

Cú pháp sử dụng:

java Copy
@RequestParam("parameterName") DataType parameterVariable

Trong đó:

  • parameterName: tên tham số trong URL query string.
  • parameterVariable: biến tương ứng nhận giá trị của tham số.

Ví dụ về URL query string: http://localhost:8080/api/v1/posts?limit=3&nextPageToken=eyJzb3J0T3JkZXIiOiJQUklPUklUWV9ERVNDX0lEX0RFU0MiLCJ2YWx1ZSI6eyJwcmlvcml0eSI6MCwiaWQiOjEyfX0=.

java Copy
package com.blog.essential.web.controller.view;

import com.blog.essential.web.controller.service.WebEssentialPostControllerService;
import com.blog.essential.web.response.WebLatestPostResponse;
import com.blog.essential.web.response.WebMostViewPostResponse;
import com.blog.essential.web.response.WebMostVotePostResponse;
import com.blog.essential.web.response.WebPostItemResponse;
import com.tvd12.ezyhttp.server.core.annotation.Controller;
import com.tvd12.ezyhttp.server.core.annotation.DoGet;
import com.tvd12.ezyhttp.server.core.annotation.RequestParam;
import com.tvd12.ezyhttp.server.core.view.View;
import lombok.AllArgsConstructor;
import org.youngmonkeys.ezyarticle.web.validator.WebTermValidator;
import org.youngmonkeys.ezyplatform.model.PaginationModel;
import org.youngmonkeys.ezyplatform.web.validator.WebCommonValidator;

import java.util.List;

import static org.youngmonkeys.ezyplatform.constant.CommonConstants.VIEW_VARIABLE_PAGE_TITLE;

@Controller
@AllArgsConstructor
public class HomeController {

    private final WebEssentialPostControllerService essentialPostControllerService;
    private final WebCommonValidator commonValidator;
    private final WebTermValidator termValidator;

    @DoGet("/")
    public View home(
        @RequestParam("author") String authorUuid,
        @RequestParam("term") String termSlug,
        @RequestParam("keyword") String keyword,
        @RequestParam("nextPageToken") String nextPageToken,
        @RequestParam("prevPageToken") String prevPageToken,
        @RequestParam("lastPage") boolean lastPage,
        @RequestParam(value = "limit", defaultValue = "1") int limit
    ) {
        this.commonValidator.validatePageSize(limit);
        this.commonValidator.validateSearchUuid(authorUuid);
        this.commonValidator.validateSearchKeyword(keyword);
        this.termValidator.validateSearchTerm(termSlug);
        WebPostItemResponse mainPost = essentialPostControllerService.getMainPostOrNull();
        List<WebPostItemResponse> extraPosts = essentialPostControllerService.getExtraPosts();
        List<WebMostViewPostResponse> mostViewPosts = essentialPostControllerService.getMostViewPosts();
        List<WebMostVotePostResponse> mostVotePosts = essentialPostControllerService.getMostVotePosts();
        PaginationModel<WebLatestPostResponse> latestPostPagination = essentialPostControllerService.getPostPagination(
            authorUuid,
            termSlug,
            keyword,
            nextPageToken,
            prevPageToken,
            lastPage,
            limit
        );
        return View.builder()
            .template("home")
            .addVariable("mainPost", mainPost)
            .addVariable("extraPosts", extraPosts)
            .addVariable("mostViewPosts", mostViewPosts)
            .addVariable("mostVotePosts", mostVotePosts)
            .addVariable("latestPostPagination", latestPostPagination)
            .addVariable(VIEW_VARIABLE_PAGE_TITLE, "home")
            .build();
    }
}

Giải Thích Các Tham Số Sử Dụng

  • author: tên tác giả, giúp lấy ra các bài viết của tác giả được chỉ định.
  • term: để lấy ra bài viết theo term.
  • keyword: dùng để lọc bài viết theo một số điều kiện cụ thể như tác giả, slug, trạng thái, loại bài viết.
  • nextPageToken: token lưu trữ vị trí bắt đầu cho lần lấy bài viết tiếp theo.
  • prevPageToken: token lưu trữ vị trí bắt đầu của lần lấy trước đó.
  • lastPage: xác định trang hiện tại có phải là trang cuối cùng không.
  • limit: số lượng bài viết mặc định khi load trang.

Ví dụ về token: {"sortOrder":"PRIORITY_DESC_ID_DESC","value":{"priority":0,"id":12}}.

Đoạn mã trên sẽ được sử dụng khi trang home được gọi và các bài viết mặc định được lấy ra. Đoạn mã dưới đây sẽ gọi khi nhấn nút load more để lấy dữ liệu bài viết tiếp theo:

java Copy
package com.blog.essential.web.controller.api;

import com.blog.essential.web.controller.service.WebEssentialPostControllerService;
import com.blog.essential.web.response.WebLatestPostResponse;
import com.blog.essential.web.response.WebPostVoteResponse;
import com.tvd12.ezyhttp.server.core.annotation.*;
import lombok.AllArgsConstructor;
import javax.servlet.http.HttpServletRequest;
import java.math.BigInteger;

@Api
@Controller("/api/v1")
@AllArgsConstructor
public class WebEssentialApiPostController {

    private final WebEssentialPostControllerService essentialPostControllerService;
    private final WebCommonValidator commonValidator;

    @DoGet("/posts")
    public PaginationModel<WebLatestPostResponse> postsGet(
        HttpServletRequest request,
        @RequestParam("author") String authorUuid,
        @RequestParam("term") String termSlug,
        @RequestParam("keyword") String keyword,
        @RequestParam("nextPageToken") String nextPageToken,
        @RequestParam("prevPageToken") String prevPageToken,
        @RequestParam("lastPage") boolean lastPage,
        @RequestParam(value = "limit", defaultValue = "3") int limit
    ) {
        this.commonValidator.validatePageSize(limit);
        this.commonValidator.validateSearchUuid(authorUuid);
        this.commonValidator.validateSearchKeyword(keyword);
        this.termValidator.validateSearchTerm(termSlug);
        return this.essentialPostControllerService
            .getPostPagination(
                authorUuid,
                termSlug,
                keyword,
                nextPageToken,
                prevPageToken,
                lastPage,
                limit
            );
    }

    @DoPost("/posts/{slug}/vote")
    public WebPostVoteResponse postsSlugVoteUpPost(
        HttpServletRequest request,
        @PathVariable String slug
    ) {
        PostModel post = postValidator
            .validatePublishedPostSlugWithTypeAndLanguage(
                slug,
                PostType.POST.toString(),
                getLanguage(request)
            );

        BigInteger totalVote = postService.increaseVoteCount(
            post.getId()
        );
        return new WebPostVoteResponse(totalVote);
    }
}

Thực Hiện Phân Trang

Từ các tham số nhận được, chúng ta sẽ truyền vào và sử dụng để xác định các tiêu chí lấy ra bài viết. Trong PostControllerService, chúng ta sẽ viết một hàm xử lý các tham số này và gọi hàm getPostPagination() để nhận được PaginationModel chứa các bài viết theo tiêu chí lọc.

Dữ liệu sẽ được thu thập và sử dụng trong hàm done() của ajax. Data tại đây tương ứng với PaginationModel, và từ data.items chúng ta sẽ lấy dữ liệu của các bài viết để xây dựng giao diện cho các bài viết tiếp theo.

Kết Luận

Phân trang bài viết là một chức năng quan trọng trong phát triển ứng dụng Blog. Nó giúp cải thiện trải nghiệm người dùng và tối ưu hóa hiệu suất của ứng dụng. Hy vọng hướng dẫn này sẽ hữu ích cho bạn trong việc triển khai tính năng này trong dự án của mình.
source: viblo

Gợi ý câu hỏi phỏng vấn
Không có dữ liệu

Không có dữ liệu

Bài viết được đề xuất
Bài viết cùng tác giả

Bình luận

Chưa có bình luận nào

Chưa có bình luận nào