使用 ngx-pagination 的服务器端分页

Posted

技术标签:

【中文标题】使用 ngx-pagination 的服务器端分页【英文标题】:Server-side pagination using ngx-pagination 【发布时间】:2020-05-01 10:40:05 【问题描述】:

我让 ngx-pagination 模块与 GET 中的所有列表一起工作,但我希望分页也能在服务器端工作,但我不确定如何进一步实现它。我正在查看 ngx-pagination 的文档,但我有点困惑。这就是我所拥有的。

html代码

<body [ngClass]="[(this.isOpen && this.mobile) || (this.isOpen && this.tablet) ? 'hideContent' : 'showContent']">
    <div class="loading">
        <!-- <mat-spinner class="loader" *ngIf="isLoading"></mat-spinner> -->

        <ngx-spinner id="loadingIcon" *ngIf="isLoading" type="cog" size="large" color="#3071a9">


            <p class="loadingTitle">Loading...</p>
        </ngx-spinner>

    </div>

    <div class="spacing"></div>
    <div class="container">
        <div class="row no-gutters"
            *ngIf="!this.isOpen && this.mobile || this.isOpen && !this.mobile || !this.isOpen && !this.mobile">
            <div class="class col-md-7"></div>

        </div>

        <!-- /|slice:0:show -->
        <!--; let i = index-->
        <div class="row"
            *ngFor="let auction of posts | paginate:  itemsPerPage: 10, currentPage: p, totalItems: this.posts.count ">
            <div class="col-md-12 col-centered">

                <div class="listingCard" [@simpleFadeAnimation]="'in'">

                    <div class=container>

                        <div class="row">
                            <div class="col-md-3">

                            </div>
                            <div class="col-md-6">
                                <div id="title">auction.title</div>
                            </div>

                        </div>

                    </div>



                </div>
            </div>

        </div>

    </div>

    <pagination-controls (pageChange)="p = $event"></pagination-controls>

</body>

</html>

<!DOCTYPE html>
<html>

<head>
</head>

<body [ngClass]="[(this.isOpen && this.mobile) || (this.isOpen && this.tablet) ? 'hideContent' : 'showContent']">
    <div class="loading">
        <!-- <mat-spinner class="loader" *ngIf="isLoading"></mat-spinner> -->
        <ngx-spinner id="loadingIcon" *ngIf="isLoading" type="cog" size="large" color="#3071a9">

            <p class="loadingTitle">Loading...</p>
        </ngx-spinner>
    </div>
    <div class="spacing"></div>
    <div class="container">
        <div class="row no-gutters"
            *ngIf="!this.isOpen && this.mobile || this.isOpen && !this.mobile || !this.isOpen && !this.mobile">
            <div class="class col-md-7"></div>
        </div>
        <!-- /|slice:0:show -->
        <!--; let i = index-->
        <div class="row"
            *ngFor="let auction of posts | paginate:  itemsPerPage: 10, currentPage: p, totalItems: this.posts.count ">
            <div class="col-md-12 col-centered">
                <div class="listingCard" [@simpleFadeAnimation]="'in'">
                    <div class=container>
                        <div class="row">
                            <div class="col-md-3">
                            </div>
                            <div class="col-md-6">
                                <div id="title">listing.title</div>
                            </div>
                        </div>
                    </div>
                    =
                </div>
            </div>
        </div>
    </div>
    <pagination-controls (pageChange)="p = $event"></pagination-controls>
</body>

.ts 文件组件

 p: number = 1;

ngOnInit()
    this.submitListingService.getListings(this.postsPerPage, this.currentPage);
    this.listingService
      .getPostUpdateListener()
      .pipe(takeUntil(this.destroy))
      .subscribe((postData:  listing: Listing[]; postCount: number ) => 
        this.isLoading = false;
        this.totalPosts = postData.postCount;
        this.posts = postData.listing;
        this.filteredPosts = postData.listing;
      );

角度服务

getListings(postsPerPage: number, currentPage: number) 
    let listings = "Get Listings";
    const params = new HttpParams().set("listings", listings);
    const queryParams = `?pagesize=$postsPerPage&page=$currentPage`;
    this.http
      .get< message: string; posts: any; maxPosts: number >(
        "http://localhost:3000/api/listings" + queryParams,
         params 
      )
      .pipe(
        map(postData => 
          return 
            posts: postData.posts.map(post => 
              return 
               title: post.title,                   
                id: post._id
              ;
            ),
            maxPosts: postData.maxPosts
          ;
        )
      )
      .pipe(takeUntil(this.destroy))
      .subscribe(transformedPostData => 
        this.posts = transformedPostData.posts;
        this.postsUpdated.next(
          listing: [...this.posts],
          postCount: transformedPostData.maxPosts
        );
      );
  

-> 服务器代码 app.js

app.get("/api/listings", (req, res, next) => 
  Post.find( auctionEndDateTime:  $gte: Date.now()  )
      .populate("creator", "username")
      .then(documents => 
        req.params.Id = mongoose.Types.ObjectId(req.params.Id);
        res.status(200).json(
          message: "Auction listings retrieved successfully!",
          posts: documents
        );
      );
);

【问题讨论】:

【参考方案1】:

这是另一种方法。 这可能更适合您的情况。

  Post.find( auctionEndDateTime:  $gte: Date.now()  )
      .populate("creator", "username")
      .then(documents => 
          req.params.Id = mongoose.Types.ObjectId(req.params.Id);

          let per_page = req.query.pagesize;
          let page = req.query.page || 1;
          let offset = (page - 1) * per_page;
        res.status(200).json(
          message: "Auction listings retrieved successfully!",
          posts: documents.slice(offset).slice(0,
                per_page)

        );
      );

这是使用slice的另一种方法

  var skip = req.query.pagesize * (req.query.page - 1)

        Post.where('auctionEndDateTime').gte(Date.now()).slice([skip, req.query.pagesize])
                      .populate("creator", "username")
                      .then(documents => 

              res.status(200).json(
                 message: "Auction listings retrieved successfully!",
                 posts: documents
              );
          )

【讨论】:

我目前有 32 个列表。当我添加您的代码时,它会抓取所有这些而不是前 10 个。如果我添加 .limit(10) 那么我只会在分页中获得页码 1。如果使用 .limit(),则没有第 2 页及以后的选项。我需要它一次只抓取 10 个,同时显示我目前不在的所有页面。这是我更新的代码pastebin.com/vJbzF3wB 服务器端有req.query.pagesizereq.query.page 吗?该解决方案应该在没有.limit 的情况下工作,但使用skiplimit 的其他解决方案也应该工作这只是一种不同的方法,但我觉得你的问题出在客户端。 我修改了您的代码并添加了限制并使其正常工作,但我认为逻辑有问题。我现在每页只能得到 10 个项目,它会像我想要的那样从服务器中获取下一页。在我的 32 个列表示例中,当我到达第 4 页时,它有 10 个项目,当我单击返回第 3 页时,它有最后 2 个?我的后端代码中有什么东西会导致这种情况吗?更新代码:pastebin.com/qdmL25CP. 不太清楚为什么,但是您编辑的解决方案破坏了我在前端的代码。我加载了您的更改,控制台说v.parent.context.$implicit.watchingGroup is undefined"(这是列表应该返回的值)pastebin.com/ap7uuzx1 您不需要第一个解决方案中的代码,只需发送文档即可【参考方案2】:

服务器端需要两个值,一个是页面,即显示哪个页码,第二个是限制,即在页面上显示多少结果。 然后只需使用:

.skip(limit * (page - 1)).limit(limit)

第一个操作员将跳过不需要的结果,例如:如果页面为 2,限制为 20,那么在第一个操作中,前 20 个结果将被跳过,然后在第二个操作中,我们将结果限制为 20 个结果,因此您将获得文档 21到 40,这是期望的结果。

【讨论】:

虽然这确实将页面限制在我设置的任何限制。比如说 10,它只显示 10 并且 ngx-paginator 变成灰色。我如何限制它,但仍然能够获得下一页的下 10 个。【参考方案3】:

您的服务文件

getListings(postsPerPage: number, currentPage: number) 
    // let listings = "Get Listings";
    // const params = new HttpParams().set("listings", listings);
    // const queryParams = `?pagesize=$postsPerPage&page=$currentPage`;
    const queryParams = postsPerPage+"/"+currentPage; //change this .
    this.http
    .get< message: string; posts: any; maxPosts: number >(
        "http://localhost:3000/api/listings/" + queryParams
        )
    .pipe(
        map(postData => 
            return 
                posts: postData.posts.map(post => 
                    return 
                        title: post.title,                   
                        id: post._id
                    ;
                ),
                maxPosts: postData.maxPosts
            ;
        )
        )
    .pipe(takeUntil(this.destroy))
    .subscribe(transformedPostData => 
        this.posts = transformedPostData.posts;
        this.postsUpdated.next(
            listing: [...this.posts],
            postCount: transformedPostData.maxPosts
        );
    );

当您发送参数时,您需要更改 API 的 URL,如下我在服务器代码中提到的,

您的服务器代码如下,

app.get("/api/listings/:postPerPage/:currentPage", (req, res, next) => 
    let postPerPage = req.params.postPerPage;
    let currentPage = req.params.currentPage;
    Post.find( auctionEndDateTime:  $gte: Date.now()  )
    .populate("creator username")
    .skip(postPerPage * currentPage)
    .limit(postPerPage)
    .then(documents => 
        req.params.Id = mongoose.Types.ObjectId(req.params.Id);
        res.status(200).json(
            message: "Auction listings retrieved successfully!",
            posts: documents
        );
    );
);

从上面的代码你会得到帖子等于 postPerPage 。 希望这是您要找的。谢谢你

【讨论】:

我添加了您的代码,目前有 12 个列表。所以最后 2 个应该在第 2 页上可用,但是 ngx-paginator 已将任何选项显示为灰色,但第 1 页。我无法转到第 2 页。如果我注释掉 .limit(),那么我可以看到第 2 页,但我不能去。我输出了单击分页器上的下一步按钮的值,它说应该是 2,但我不在第 2 页上,除非我刷新页面,否则我不能回到 1。 (点击分页器时列表永远不会改变)这是对html的引用pastebin.com/9pzVidJ6

以上是关于使用 ngx-pagination 的服务器端分页的主要内容,如果未能解决你的问题,请参考以下文章

ngx-pagination:Angular 6 服务器端实现

引导表服务器端分页

使用 DataTables 插件的服务器端分页

如何使用 NestJS 提供服务器端分页?

使用 ejs 模板和 MYSQL 在节点 js 中的服务器端分页

JQuery DataTables 服务器端分页