Parsware
All articles

Parsware Platform

A list a page at a time

A drawn list that shows ten records at a time: a page number in the screen's state, one query that skips to it and counts the whole table, and Previous and Next buttons that switch themselves off at either end.

The Contacts screen in the Shell: "100 contacts · page 2", Previous and Next buttons, and ten contacts from Avery Williams down, next to the title "A list a page at a time"

Show records on a screen loaded the first 50 contacts and let the list scroll. That works for 50. It doesn't work for 5,000: the screen would ask for all of them every time it opened, and the reader would scroll forever.

This article turns the same list into pages of ten, with a count of how many there are in total and Previous and Next buttons that stop at either end. We kept building the Contact list app from the last article, and took its Table view out so the drawn list has the screen to itself.

1. Three more state fields

Select the Contacts screen and add three state fields on its Screen tab:

Field Starts as Holds
page 0 which page is showing, counting from 0
pageSize 10 how many rows a page has
total 0 how many contacts there are altogether

The Contacts screen's state: contacts [], page 0, pageSize 10 and total 0

2. Ask for one page, and count them all

Press Write code, choose Screen opens, and replace what's there with:

loadRecords("contact", "contacts", {
  top: getState("pageSize"),
  skip: getState("page") * getState("pageSize"),
  orderBy: "fullname",
  countInto: "total"
});

The code page for the screen: Screen opens marked Written, with the loadRecords call over five lines

Two parts are new:

  • skip leaves out that many records before the page starts. Page 0 skips none, page 1 skips ten, page 2 skips twenty.
  • countInto names a state field to put the total in: how many records match, not just how many came back. A page of ten can't tell you whether it's the last ten or the first of a hundred. This can.

A handler reads state with getState("page"). A binding reads it as state.page. Both mean the same field.

3. Say where you are

Select the Count paragraph from the last article and change its Text binding to:

text(state.total) + " contacts · page " + text(state.page + 1)

The code page for Count: Text marked Written, with the binding in the editor

The + 1 is for people: the screen counts pages from 0, but nobody calls the first page "page 0".

4. Previous and Next

Place two Button controls beside the count. We put Previous at X 284 and Next at X 396, both at Y 100, 100 wide and 36 tall, and typed their words into the text inside each one.

Each button gets two pieces of code. Its Pressed handler moves the page and asks again:

setState("page", getState("page") - 1);
loadRecords("contact", "contacts", {
  top: getState("pageSize"),
  skip: getState("page") * getState("pageSize"),
  orderBy: "fullname",
  countInto: "total"
});

The code page for Previous: Pressed marked Written, with setState and loadRecords in the editor

Next is the same with + 1. The lines run in the order they're written, so loadRecords sees the page that setState just set.

The second piece is an Enabled binding, which says when the button can be pressed:

  • Previous: state.page > 0
  • Next: (state.page + 1) * state.pageSize < state.total

The code page for Next: Enabled marked Written, with the comparison in the editor

Next is on while the rows shown so far are fewer than the total. On the tenth page state.page is 9, (9 + 1) * 10 < 100 is false, and the button switches itself off. That's why countInto matters: without the total, Next would have to be pressed to find out there was nothing after it.

5. Make the list fit a page

A page of ten rows should fit in the list without scrolling. Our rows are 56 tall, so in the list's Graphic tab we set Gap to 4, Padding to 0 and H to 596: ten rows and nine gaps. Leave Content that does not fit at Scroll to it anyway, in case a row ever grows.

6. Open it in the Shell

Save, and open Contact list from the Shell. Page 1, with Previous off because there's nothing before it:

Contacts in the Shell: 100 contacts · page 1, Previous greyed out, Next lit, and ten contacts from Alex Anderson to Avery Williams

Press Next nine times and you reach the end. Next is off, Previous is on:

Contacts on the last page: 100 contacts · page 10, Previous lit, Next greyed out, and ten contacts from Taylor Garcia to Yuki Williams

Each press asks the environment for ten rows, so the screen stays quick however big the table gets.

Search, too

The Studio records sample (studio-records) adds a search box to the same query:

contains: { fullname: getState("query") },

and its Search button sets page back to 0 before it loads. A search that stayed on page 4 would usually show an empty page, because the matches fit on the first. The search runs in the environment, not in the browser: filtering the ten rows already on screen could never find the eleventh contact.

Try it

Make the page size a choice. Place a Dropdown with the options 10, 25 and 50, bind its Value to a state field, and in its Value changed handler copy that into pageSize with num(…), set page to 0 and load again.

Next: a form that saves a record, beside this list.