Currently, there are about 150 records being viewed. Technically, it's not a problem, but what if we have double that number? Ten times?
Visually, any number of records that requires the user to scroll, is potentially a problem. Thus, we need a mechanism in which the user can limit the page size to a number that they're comfortable with.
pageSize has already been set at a default of 50 earlier; we will keep that default and add the mechanism to change it.
For this, we need some HTML inside the div. It's a
select tag, with the options 20, 50, 100 and 500. 50 is selected by default.
<div id="listPaging">
<hr />
<div>
Records per Page
<select>
<option value="20">20</option>
<option value="50" selected>50</option>
<option value="100">100</option>
<option value="500">500</option>
</select>
</div>
</div>
For what it's worth, this is how the selector looks right now. Note that we still show 149 records even though page size is 50.
On changing the value in this drop-down list, we run the
setPageSize() method and pass in the value as an argument.
<div id="listPaging">
<hr />
<div>
Records per Page
<select onchange="lv.setPageSize(this.value)">
<option value="20">20</option>
<option value="50" selected>50</option>
<option value="100">100</option>
<option value="500">500</option>
</select>
</div>
</div>
We'll create
setPageSize(). In there, we start by setting
pageSize to the argument passed in, and
pageNo to 1. We end, of course, with a
return statement.
const lv =
{
pageSize: 50,
pageNo: 1,
sort: [],
search: "",
records: [],
getRecords: function(year = 2025, month = 3)
{
$.ajax(
{
url: `https://oracleapex.com/ords/teochewthunder/borrowing/${year}/${month}`,
method: "GET",
success: function(data)
{
lv.records = data.items;
lv.filterRecords();
return;
},
error: function (xhr, status, error)
{
console.log(xhr);
}
}
);
},
setPageSize: function(pageSize)
{
this.pageSize = pageSize;
this.pageNo = 1;
return;
},
filterRecords: function()
{
And after that's done, we run
filterRecords() to display the records with the new value of
pageSize.
setPageSize: function(pageSize)
{
this.pageSize = pageSize;
this.pageNo = 1;
this.filterRecords();
return;
},
Now it's time to tweak
filterRecords() to honor
pageSize. Before iterating through
filtered, we first reduce the size of
filtered. We begin by declaring
startIndex. The page we're on will determine what record we start from - the first record of the current page. If the current page is 1, of course we'll start from the very first record at index 0. If the current page is 2 and page size is 20, we'll start at record 20. Because page 1 is records 0 to 19.
filterRecords: function()
{
$("#listRows").html("");
var filtered = structuredClone(this.records);
var startIndex = ((this.pageNo - 1) * this.pageSize);
filtered.forEach(
(x, i) =>
{
const row = $("<div></div>");
row.addClass("listViewColumns");
if (i % 2 == 0) row.addClass("shade");
And then filtered will have the
slice() method run on it. We'll only want to view
pageSize records. Thus, we slice from
startIndex to
startIndex plus
pageSize.
filterRecords: function()
{
$("#listRows").html("");
var filtered = structuredClone(this.records);
var startIndex = ((this.pageNo - 1) * this.pageSize);
filtered = filtered.slice(startIndex, startIndex + parseInt(this.pageSize));
filtered.forEach(
(x, i) =>
{
const row = $("<div></div>");
row.addClass("listViewColumns");
if (i % 2 == 0) row.addClass("shade");
Now you'll see that only 50 records are shown, because default page size is 50!
If you adjust it to 20, the view shrinks to 20 records!
Time to control pageNo!
Add this div, and these button and
span tags inside that div. These buttons - styled using the
pageButton CSS class - are for first, last, previous and next page, not in that order. In between, we have three
span tags which are placeholders for the values later. Take note of the ids - I think they're aptly named and very self-explanatory.
<div id="listPaging">
<hr />
<div>
<button id="btnFirst" class="pageButton">«</button>
<button id="btnPrev" class="pageButton">‹</button>
<span id="pnlPageNo"></span>
<span>/</span>
<span id="pnlPages"></span>
<button id="btnNext" class="pageButton">›</button>
<button id="btnLast" class="pageButton">»</button>
</div>
<div>
Records per Page
<select onchange="lv.setPageSize(this.value)">
<option value="20" >20</option>
<option value="50" selected>50</option>
<option value="100">100</option>
<option value="500">500</option>
</select>
</div>
</div>
As for the CSS, here's the styling for
pageButton. All of it visual. I've even ensured that it turns
red on mouseover.
.figure
{
text-align: right;
}
.pageButton
{
width: 20px;
height: 20px;
font-size: 1.2em;
background: transparent;
border: none;
color: rgb(0, 0, 0);
}
.sortButton, .noButton
{
width: auto;
padding: 0px;
height: 1.5em;
font-size: 1em;
background: transparent;
border: none;
color: rgb(0, 0, 0);
}
.pageButton:hover
color: rgb(255, 0, 0);
}
</style>
You can see the new arrows now.
Just a bit of a nudge. Add a couple spaces after the drop-down list...
<div id="listPaging">
<hr />
<div>
<button id="btnFirst" class="pageButton">«</button>
<button id="btnPrev" class="pageButton">‹</button>
<span id="pnlPageNo"></span>
<span>/</span>
<span id="pnlPages"></span>
<button id="btnNext" class="pageButton">›</button>
<button id="btnLast" class="pageButton">»</button>
</div>
<div>
Records per Page
<select onchange="lv.setPageSize(this.value)">
<option value="20" >20</option>
<option value="50" selected>50</option>
<option value="100">100</option>
<option value="500">500</option>
</select>
</div>
</div>
...and float them right.
#listPaging
{
width: 100%;
}
#listPaging div
{
float: right;
}
.figure
{
text-align: right;
}
Looks neater now.
What we will do next, is assign click actions to methods. There are two methods here.
setPageNo() is an atomic method that can be used on its own. It accepts one argument, which is the page number to set.
setPage() is a bit more abstract. The argument is a string that is a relative value - "last", "prev" or "next". I did not bother with "first" because that's always page 1.
<div>
<button id="btnFirst" class="pageButton" onclick="lv.setPageNo(1)">«</button>
<button id="btnPrev" class="pageButton" onclick="lv.setPage('prev')">‹</button>
<span id="pnlPageNo"></span>
<span>/</span>
<span id="pnlPages"></span>
<button id="btnNext" class="pageButton" onclick="lv.setPage('next')">›</button>
<button id="btnLast" class="pageButton" onclick="lv.setPage('last')">»</button>
</div>
Now let's create these methods with
return statements.
getRecords: function(year = 2025, month = 3)
{
$.ajax(
{
url: `https://oracleapex.com/ords/teochewthunder/borrowing/${year}/${month}`,
method: "GET",
success: function(data)
{
lv.records = data.items;
lv.filterRecords();
return;
},
error: function (xhr, status, error)
{
console.log(xhr);
}
}
);
},
setPage: function(index)
{
return;
},
setPageNo: function(pageNo)
{
return;
},
setPageSize: function(pageSize)
{
this.pageSize = pageSize;
this.pageNo = 1;
this.filterRecords();
return;
},
Let's work on
setPageNo() first. There's going to be a new variable,
finalPageNo. This is the value of
pageNo, the parameter accepted in the method, after it's been through a few checks. For this, we next define
pages, which is the value returned by calling
getPages.
setPageNo: function(pageNo)
{
let finalPageNo = pageNo;
let pages = this.getPages();
return;
},
Create
getPages(). This one is simple. It takes the total number of records and divides it by the number of records per page, which gives us the total number of pages. We want to round up this number, so using the
ceil() method of
Math is appropriate here.
getRecords: function(year = 2025, month = 3)
{
$.ajax(
{
url: `https://oracleapex.com/ords/teochewthunder/borrowing/${year}/${month}`,
method: "GET",
success: function(data)
{
lv.records = data.items;
lv.filterRecords();
return;
},
error: function (xhr, status, error)
{
console.log(xhr);
}
}
);
},
getPages: function()
{
return (Math.ceil(this.records.length / this.pageSize));
},
setPage: function(index)
{
return;
},
setPageNo: function(pageNo)
{
let finalPageNo = pageNo;
let pages = this.getPages();
return;
},
Now that you have
pages, you check if
pageNo is less than 1, and ensure that the minimum value is 1. Similarly, you ensure that the maximum value is
pages.
setPageNo: function(pageNo)
{
let finalPageNo = pageNo;
let pages = this.getPages();
if (finalPageNo < 1) finalPageNo = 1;
if (finalPageNo > pages) finalPageNo = pages;
return;
},
Then you set
pageNo to
finalPageNo, run
filterRecords(), and run
renderPageInfo().
setPageNo: function(pageNo)
{
let finalPageNo = pageNo;
let pages = this.getPages();
if (finalPageNo < 1) finalPageNo = 1;
if (finalPageNo > pages) finalPageNo = pages;
this.pageNo = finalPageNo;
this.filterRecords();
this.renderPageInfo();
return;
},
Hang on, what's renderPageInfo()?
Well, that's the method we will create next. It renders the display of the number of pages and current page. It will be run in a few places, not just
setPageNo().
prettifyDate: function(dt)
{
var arr = dt.split("T");
return arr[0];
},
renderPageInfo: function()
{
return;
}
};
It's not too complex. Just populate
pnlPageNo with the value of
pageNo and
pnlPages with the total number of pages. Note that we just call
getPages() here.
prettifyDate: function(dt)
{
var arr = dt.split("T");
return arr[0];
},
renderPageInfo: function()
{
$("#pnlPageNo").text(this.pageNo);
$("#pnlPages").text(this.getPages());
return;
}
};
Now for
setPage()! You'll see that it has a parameter,
index. That's the relative value I mentioned earlier. We have a few
If blocks here, questioning the value of
index.
setPage: function(index)
{
if (index == "next")
if (index == "prev")
if (index == "last")
return;
},
This is fairly straightforward at this point. If you want the next page, run s
etPageNo() with an incremented value of
pageNo, and let
setPageNo() do its thing. Similarly for the previous page. As for the last page, we'll run
getPages() here again.
setPage: function(index)
{
if (index == "next") this.setPageNo(this.pageNo + 1);
if (index == "prev") this.setPageNo(this.pageNo - 1);
if (index == "last") this.setPageNo(this.getPages());
return;
},
We'll need to run
renderPageInfo() in
getRecords() too.
getRecords: function(year = 2025, month = 3)
{
$.ajax(
{
url: `https://oracleapex.com/ords/teochewthunder/borrowing/${year}/${month}`,
method: "GET",
success: function(data)
{
lv.records = data.items;
lv.filterRecords();
lv.renderPageInfo();
return;
},
error: function (xhr, status, error)
{
console.log(xhr);
}
}
);
},
And
setPageSize().
setPageSize: function(pageSize)
{
this.pageSize = pageSize;
this.pageNo = 1;
this.filterRecords();
this.renderPageInfo();
return;
},
And before I forget, in
filterRecords(), we'll need to cater for new page numbers, so the record numbering will also depend on what page is being viewed.
col = $("<div></div>");
col.text(i + 1 + startIndex);
col.addClass("figure");
row.append(col);
Try it!
On the first load, default page Size is 50. See? You're viewing page 1 of 3, because there are 149 records.
Change page size to 20. Now you have 8 pages!
Click the next page. You're now viewing record 21 to 40!
Click the last page. Now you're viewing records 141 to 149!
This was a little long. Thanks for bearing with me. The next part will be just as complicated, but hopefully shorter!
Next
Sorting records.