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:
- Sorts the results on a give attribute (ascending or descending)
- Returns a single page, limited to a maximum number of tuples
- Allows a key value to be passed to jump to a page after a given position (next) or before a given position (prev)
- Returns a prev_page and a next_page position that can be passed back into a subsequent paginate call to navigate forwards or backwards through the results
- Allows the attributes to be given a known left-to-right ordering (one that the render function will respect)
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
| SNO | SNAME | STATUS | CITY |
|---|---|---|---|
| S1 | Smith | 20 | London |
| S2 | Jones | 10 | Paris |
| S3 | Blake | 30 | Paris |
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.