Data Serving

Pagination

Using templates to render ordered tables as HTML gives us a powerful presentation system for our query results. We've used the template-based render function to render data as an HTML table. Next we'll split the results into separate pages so we don't overwhelm the user.

We can use the paginate function to split the results. It does the following for us:

Here's the previous code, slightly modified, that will call paginate on the results before passing the resulting table to the template render function. The example uses an unrealistic 3 tuples per page.

import("http")
	
route := {path:str,   relgen:str}{
		 ["/",        "index_handler"]
}

index_handler := {attr_types(http.hh)} begin 
    mydata := paginate($S, {SNO}, 3, {SNO, SNAME, STATUS, CITY}, form[k="p"], form[k="n"])
	response := http.response{*,
		body:=template_execute("b1", "hello2", mydata)
	}
	yield
end

main := {} begin
	template_register("b1", dee(
		template_name:="hello2",
		template_content:=
		«<!doctype html><h1>Suppliers</h1>
« render .data »"
<a href="?p=« .prev_page »">Prev</a> <a href="?n=« .next_page »">Next</a>
»
	))

	res := http.serve(route:=route)
	print(res)
end

Notice the added call to paginate in the index_handler:

mydata := paginate($S, {SNO}, 3, {SNO, SNAME, STATUS, CITY}, form[k="p"], form[k="n"])

We pass in two form relations taken from the passed in http.hh request header form. These have a key (k) of "p" and "n", short for "prev" and "next". The paginate function takes the form relations and uses them to jump to the requested position in the sorted results.

The paginate function returns a tuple{data:table{...}, prev_page:str, next_page:str}, where data is the table to be rendered and prev_page and next_page are the contents for the Next and Prev links that will drive any further navigation.

When form is empty (e.g. on first load) this example will return the following tuple:

┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄
  data                                prev_page   next_page   
  #################################               SNO:str=S3  
    SNO   SNAME   STATUS   CITY                               
    S1    Smith       20   London                             
    S2    Jones       10   Paris                              
    S3    Blake       30   Paris                              
  #################################                           
┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄

We pass the whole result tuple into the template_execute function. The render template function now renders .data, i.e. the attribute within the results of the paginate function, which contains the Ra table.

Also notice the extra two <a> links below the render template function. These pass the other two attributes returned from the paginate function, namely prev_page and next_page which can direct the next call to paginate. The ?p= and ?n=, when clicked by the user, will make their way through the browser to the form relvar in the index_handler.

In a browser, hitting http://localhost:8080 would now give:

Suppliers

SNOSNAMESTATUSCITY
S1Smith20London
S2Jones10Paris
S3Blake30Paris
Prev Next

This HTML table can now be navigated in chunks using the Prev and Next links (though the links won't work in the example above - it's just for display). We've not done any CSS styling of the links but that, and hiding them if they're empty, should be straightforward.

The table now has a well defined left-to-right ordering and can support results with lots of tuples.

Next we'll see how we can use a third-party library, htmx, to improve things even more.