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>
<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>
<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()
{
{
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;
},
{
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");
{
$("#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");
{
$("#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>
<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>
{
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>
<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;
}
{
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>
<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;
},
{
$.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;
},
{
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;
},
{
$.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;
},
{
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;
},
{
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;
}
};
{
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;
}
};
{
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;
},
{
if (index == "next")
if (index == "prev")
if (index == "last")
return;
},
This is fairly straightforward at this point. If you want the next page, run setPageNo() 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;
},
{
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);
}
}
);
},
{
$.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;
},
{
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);
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!









No comments:
Post a Comment