If you studied some front-end framework tutorials, you might have noticed that To-Do-Lists are kind of the new "Hello World" in Web App programming. So I guess I should include one too. We will discuss two variants:
- A desktop version using the
+Chartclass, which allows a very efficient and fast definition of the code, although the look is a little bit outdated.
- A more mobile-friendly, but much less powerful approach using HTML lists.
Let's start with the desktop version. It is created out of pre-defined components - in total, there are only 14 lines of code.
The hosted version can be found here.
Before we start with the actual program, we need some background information about the
+Chart is a powerful class that maintains and displays the internal representation of two-dimensional data. The data is usually organized in HTML tables with rows and columns, and we can use pre-defined buttons to delete and move rows and scroll up and down. Usually it is used together with the PicoLisp database (we'll come to that point later).
Let's study its functionality directly at a little example.
Creating a To Do list
Let's say we want to create a To-Do List where each item has two fields:
Date. The list should be of arbitrary length. This is all we need:
(app) (action (html 0 "Todo" "@lib.css" NIL (form NIL (gui '(+Var +Chart) '*ToDoList 4) (<table> NIL "Todo" '((NIL "Item") (NIL "Date") (btn) (btn)) (do 6 (<row> NIL (gui 1 '(+TextField) 30) (gui 2 '(+DateField) 10) (gui 3 '(+DelRowButton)) (gui 4 '(+BubbleButton)) ) ) ) (scroll 6 T) ) ) )
In only 14 lines of code, we can create a full To Do App.
As you could see at the GIF above, the data is directly processed once the input is done (note how the date is changing the format!). You can navigate within the fields with mouse / arrow keys and
<tab>. We have several kinds of buttons that allow us to move data up and down or delete items.
In the terminal window where the server is running, let's check the value of our global variable
*ToDoList. It should look something like this:
$ pil @lib/http.l @lib/xhtml.l @lib/form.l --server 8082 + 6572 = 37441 12235747877912233~ : -> (("Create seminar abc" 738389) ("Call customer Mr. X" 738391))
What does that show us? First thing we can see is that a new session was started (port
37441, session ID
12235747877912233~). Then, secondly, we can access the content of
*ToDoList and see that it's a simple list where each item has the structure
Analyzing the source code
Now let's take a look at the source code. First thing inside our
form function is a
(gui '(+Var +Chart) '*ToDoList 4)
This tells the interpreter that all following gui elements shall be managed by that Chart, if their first argument is a number. We see a prefix class
+Var which is used to bind a variable to the chart - in our case our global
ToDoList. The class
+Chart takes a numeric argument which represents the number of columns.
After that comes a tag function
<table> which is defined in the
xhtml.l library is not documented (yet), but we can check out the source code:
$ pil @lib/http.l @lib/xhtml.l @lib/form.l + : (vi '<table>)
We see that
<table> takes up to four arguments:
(<table> NIL "Todo" '((NIL "Item") (NIL "Date") (btn) (btn)) ... )
From our example:
Attr-> NIL (the CSS attributes)
Ttl-> "Todo" (the title)
'((NIL "Item") (NIL "Date") (btn) (btn))
Prg-> all the rest.
"Head" takes two arguments per column: the first one is the class name, the second one the string (both are optional).
Then the row content is defined:
(do 6 (<row> NIL (gui 1 '(+TextField) 30) (gui 2 '(+DateField) 10) (gui 3 '(+DelRowButton)) (gui 4 '(+BubbleButton))
This is pretty straightforward - we create 6 rows (
do 6), each with the content defined inside
<row> has no CSS attributes and four fields:
The index number defines which field goes into which gui element. For example, if we switch the first two lines,
(gui 2 '(+TextField) 30) (gui 1 '(+DateField) 10)
our rendered table will still look the same, but in our
*ToDoList, the date will be placed before the item description.
In the last line, we find the following code:
(scroll 6 T)
which corresponds to the navigation buttons:
Again, we check the source code in
(de scroll (N Flg) (when Flg (gui '(+Tip +GoButton) ,"Go to first line" 1 "|<") ) (gui '(+Tip +UpButton) ,"Scroll up one page" N "<<") (gui '(+Tip +UpButton) ,"Scroll up one line" 1) ...
It's pretty straightforward: If the second argument is non-
NIL, then we can define how many items are defined per "page" (i. e., the number of columns that are currently displayed).
Why did we call this "Desktop version"?
You might agree with me that the design of the output table looks very much like enterprise software like SAP (for example the button design). Furthermore the table size is fixed - if you decrease the window size or access the page from a mobile screen, you need to scroll. Depending on the application, this can be a problem or not.
It is also difficult to style the table with pre-defined classes from frameworks like bootstrap, because those can easily break the table structure.
If we want to change this design fundamentally, we would need to go quite deep into definition of those components. Let's say for example, we want to replace the
^ button label by icons. The whole layout of the buttons is pre-defined, which means we need to go down to source-code level to modify it.
The good news is that it is absolutely possible, since the source code is open and not very complicated. For example, in order to change the
<label of the scroll button to an icon, we only need to modify the
(dm T) function of the
(dm T (Exe Lbl) (super '(> (chart 'ofs) 1) (or Lbl "<") (list 'scroll> (lit "*Chart") (list '- Exe)) ) )
If you plan to write an app that utilizes large amount of data and are not happy with the current table design, I guess I would recommend to go that way.
For now, let's leave it like this. We will come back to this example later when we show how to store and retrieve data from the PicoLisp database, and maybe also add some user identification and so on.
The sourcecode of this example can be found here.
In the next post, we will try to re-create this example without using
+Chart. The design will be a little bit more fresh, but obviously we will also need to put a little more work into it.