-
Notifications
You must be signed in to change notification settings - Fork 804
Custom pager with only next previous mode (version 2)
!!! Note: This is version 2 of this post: https://github.com/serenity-is/Serenity/wiki/Grid---Mixin---Custom-grid-pager-without-counting-total-records ...
In first version, beause I need to stop total row counting to optimize speed of grid (count total records is a heavy job and will reduce performance when you have a table with a lot records), I created a new pager with support only next/previous button
The pager transfer pager's information between serenity original pager vs custom pager. This is truly a pain for maintaining code. If you look at code of version 1, it is really ugly ^^
Version 1 you also write custom code in repository.cs class (or YourEntityListHandler.cs in new Serenity version), if you implement the next/previous pager then you have to re-write code for any page you want to apply, that is bored.
Also it does not know if the last page is exactly the last page or not. For example, if page size is 10, and you have total 30 rows, then last page is page 3, but it does not know if there is a next page or not because fetched entities of page 3 equals to pageSize ( = 10). Therefor, if you want to disable next page button when it hits the last page then you can not.
To handle that problem and make code cleaner I decided to use directly serenity's original pager with a bit custom about UI.
Firstly, you will need to create 2 files, one for OnlyNextPreviousPagerMixin.ts
and one for UseNextPreviousPagerBehavior.cs
(yes, this times we will use Serenity Behavior feature)
In this version the idea is we will take more 1 record to check if database still has next page, for example, instead take 20 rows as normal, we will take 21 rows, if database returns 21 row, it means we still have next page, if it just return 20 rows or less, it means current page is last page
UseNextPreviousPagerBehavior.cs
using Serenity.Data;
using Serenity.Services;
using System;
using System.Collections.Generic;
namespace PUT_YOUR_NAMESPACE_HERE
{
public class MyBaseListRequest : ListRequest
{
public bool EnablePagerOnlyNextPreviousMode { get; set; }
}
public interface IUseNextPreviousPager
{
}
public class UseNextPreviousPagerBehavior : IImplicitBehavior, IListBehavior
{
IRow _row;
public bool ActivateFor(IRow row)
{
var mt = row as IUseNextPreviousPager;
if (mt == null)
return false;
_row = row;
return true;
}
public void OnAfterExecuteQuery(IListRequestHandler handler)
{
}
public void OnApplyFilters(IListRequestHandler handler, SqlQuery query)
{
if (handler.Request is MyBaseListRequest customRequest)
{
if (customRequest.EnablePagerOnlyNextPreviousMode)
{
var oldTake = handler.Request.Take;
query.Skip(handler.Request.Skip);
query.Take(oldTake + 1);
// Setting CountRecords to false stops the count(*) query from running
query.CountRecords = false;
}
}
}
public void OnBeforeExecuteQuery(IListRequestHandler handler)
{
}
public void OnPrepareQuery(IListRequestHandler handler, SqlQuery query)
{
}
public void OnReturn(IListRequestHandler handler)
{
if (handler.Request is MyBaseListRequest customRequest)
{
if (customRequest.EnablePagerOnlyNextPreviousMode)
{
//Type generic = typeof(ListResponse<>);
//Type[] typeArgs = { _row.GetType() };
//Type constructed = generic.MakeGenericType(typeArgs);
//var instance = Activator.CreateInstance(constructed);
//dynamic response = Convert.ChangeType(handler.Response, instance.GetType());
dynamic response = Convert.ChangeType(handler.Response, handler.Response.GetType());
response.TotalCount = int.MaxValue;
if (response.CustomData == null)
{
response.CustomData = new Dictionary<string, object>();
}
response.CustomData.Add("J_IsFirstPage", handler.Request.Skip == 0);
if (handler.Response.Entities.Count > handler.Request.Take)
{
response.CustomData.Add("J_OnlyNextPreviousMode_HasNextPage", true);
//var items = handler.Response.Entities.Cast<IRow>();
handler.Response.Entities.RemoveAt(handler.Request.Take);
}
}
}
}
public void OnValidateRequest(IListRequestHandler handler)
{
}
}
}
OnlyNextPreviousPagerMixin.ts
namespace SimpleFeedly {
export class OnlyNextPreviousPagerMixinOptions {
grid: Serenity.DataGrid<any, any>;
}
export class OnlyNextPreviousPagerMixin {
option: OnlyNextPreviousPagerMixinOptions;
constructor(opt: OnlyNextPreviousPagerMixinOptions) {
this.option = opt;
this.initPager();
}
private initPager() {
this.option.grid.element.find(".slick-pg-stat").remove();
this.option.grid.element.find(".slick-pg-total").remove();
this.option.grid.element.find(".slick-pg-first").remove();
this.option.grid.element.find(".slick-pg-last").remove();
this.option.grid.element.find(".slick-pg-control").contents().each(function () {
//Text node
if (this.nodeType === 3 && Q.trimToEmpty($(this).text()) == "/") {
$(this).remove();
}
});
this.option.grid.element.find(".slick-pg-next").addClass("pager-button-disabled");
this.option.grid.element.find(".slick-pg-prev").addClass("pager-button-disabled");
this.option.grid.element.find(".slick-pg-first").addClass("pager-button-disabled");
(this.option.grid.view.params as any).EnablePagerOnlyNextPreviousMode = true;
}
public handleOnViewProcessData(response: Serenity.ListResponse<any>) {
let hasNextPage = (response as any)?.CustomData?.J_OnlyNextPreviousMode_HasNextPage ?? false;
let isFirstPage = (response as any)?.CustomData?.J_IsFirstPage ?? false;
this.option.grid.element.find(".slick-pg-next").toggleClass("pager-button-disabled", !hasNextPage);
this.option.grid.element.find(".slick-pg-prev").toggleClass("pager-button-disabled", isFirstPage);
}
}
}
You also need a css class to (fake) disable buttons:
.pager-button-disabled {
pointer-events: none;
opacity: 0.3;
}
Put bellow into your grid.ts
/// <reference path="../../Common/Mixins/OnlyNextPreviousPagerMixin.ts" />
namespace PUT_YOUR_NAMESPACE_HERE {
@Serenity.Decorators.registerClass()
export class YOUR_GRID extends Serenity.EntityGrid<any, any> {
// ....
// ....
private onpp: OnlyNextPreviousPagerMixin; // <===================== ADD THIS LINE
constructor(container: JQuery) {
super(container);
this.onpp = new OnlyNextPreviousPagerMixin({ grid: this }); // <===================== ADD THIS LINE
}
protected onViewProcessData(response) {
var lr = super.onViewProcessData(response);
this.onpp.handleOnViewProcessData(response); // <===================== ADD THIS LINE
return lr;
}
}
}
Finally, add IUseNextPreviousPager
into your Row.cs
, for example:
public sealed class YOUR_ROW : Row<YourEntity.RowFields>, IIdRow, INameRow, IUseNextPreviousPager // <========== ADD HERE
{
[DisplayName("Id"), Identity, IdProperty]
public Int64? Id
{
get { return fields.Id[this]; }
set { fields.Id[this] = value; }
}
Let me know ( email to [email protected] ) if you have any ideas to improve this custom pager, thank you
Copyright © Serenity Platform 2017-present. All rights reserved.
Documentation | Serene Template | Live Demo | Premium Support | Issues | Discussions