Creating a User Interface for Data Modification, Part 1
Tags: picolisp, functional, database, lisp
Published: 2022-05-06 08:29:30

Of course we don't only want to be able to display the data, but also modify it. In this post, we will use the PicoLisp GUI to display the data and enable the data modification in a HTML form sheet.

In the first step, we will simply build a page that allows us to display the data and jump between the records:

![familytable.gif](https://cdn.hashnode.com/res/hashnode/image/upload/v1634578620075/lQRPL7S7K.gif)

-----------------------

### Set-up of the file

The structure of the program builds up on the previous post, which is used as a basis. We will use [this file](https://gitlab.com/picolisp-blog/web-applications/-/blob/main/database/family-treeview.l) and extend it.

-----------------------

Let's create a new function called ``person`` and add it to the ``allowed`` script. Also let's start the server with it. The rest stays the same. We call the new file ``family-edit.l``.

```
### Modify / add following lines:

(allowed ("css/")
   "@lib.css" "!treeReport" "!person")
...

(de person ())
...

(de go ()
   (rollback)
   (server 8080 "!person") )
```

The function ``person`` shall be the function where we can display all relevant data about a record (i. e. a person) at one glance. We want to display the data as a form field, and be able to toggle between read and edit mode using an "Edit" button.

----------------------------

The first thing we need is to create the standard HTML function including the session start-up. On this page, we will not use the bootstrap CSS classes because we will use many pre-defined PicoLisp gui functions that don't work well with Bootstrap.
 
```
(de person () 
   (app)
   (action
      (html 0 "Person" "@lib.css" NIL
         (form NIL
]
```

*The bracket ``]`` closes all open parentheses. This can be useful in the development.*

Like in the previous examples, we can start it with ``$ pil family-edit.l -family~main -go +`` and when we point the browser to http://localhost:8080, we should see an empty tab with title "Person".

------------------------

### Getting the database item

How does the form "know" which database item we want to display? This is controlled by **calling the ``<id>`` function** somewhere within the form. ``<id>`` binds the output to the global variable ``*ID``. So we need to do two things: 
1. Adding ``<id>`` to our form,
2. Appending an ``*ID`` parameter to the URL.

For example, we could call ``<id>`` in the title of our form. ``( <id> (: nm ))`` will set ``*ID`` to the current object and display its name.

```
(form NIL
   (<h2> NIL (<id> (: nm)))
```

If we point the browser now to http://localhost:8080/?*ID=-A1, we see this:


![idfield.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1634556217301/mz4p9qNUI.png)

-------------------------

### The ``+E/R`` prefix class

Now that we have specified the current object, we can display its data in a form. In order to access the database object, we will use the ``+E/R`` and ``+Obj`` prefix classes.

*[Read here](https://picolisp-blog.hashnode.dev/web-application-programming-in-picolisp-introducing-the-gui-framework) for an introduction to the PicoLisp Form GUI and [here](https://picolisp-blog.hashnode.dev/web-application-programming-in-picolisp-prefix-classes) for more on Prefix classes.*

Just like we can add styles using ``+Styles`` and bind variables to a GUI element using ``+Val``, we can also connect a database entity using the ``+E/R`` class. The syntax is pretty straightforward:

```
(gui '(+E/R +TextField) '(nm : home obj) 40 "Name")
```

This creates a textfield which is connected to the ``nm`` ("name") property of the ``home obj``. The ``home obj`` is the object that belongs to the current form, i. e. the record that is specified via the global variable ``*ID``. 

Analogously, we can get the birth and death date like this:

```
"born" (gui '(+E/R +DateField) '(dat : home obj) 10)
"died" (gui '(+E/R +DateField) '(fin : home obj) 10)
```

![diana.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1634578775966/f9YtpsWvt.png)


------------------------

### The ``+ClassField`` form element 

The ``+ClassField`` form element can be used to display and change the class of an object. For example, this example renders a drop-down menu which shows either "Male" or "Female" as current value:

```
(gui '(+ClassField) '(: home obj) '(("Male" +Man) ("Female" +Woman))) )
```

![class.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1634578726456/ddlSG-n6S4.png)

-------------------------

### The ``+Obj`` Prefix class

The ``+Obj`` prefix class is used to hold a specific object. This way, a DB object becomes a first class GUI item just like the primitives string, number etc. In addition, ``+Obj`` supplies suggestions from the database, using ``dbHint``.

What does that mean? Let's take the person's partner field as an example. The person's partner is also an object, connected by the property ``mate``. Since we use ``+Obj``, we can now access the record's name like this:

```
(gui '(+E/R +Obj +TextField) '(mate : home obj) '(nm +Person) 30) )
```

![objclass.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1634578816180/90YGcmwci.png)

---------------------

``+Obj`` also offers two nice additional features: 
1. Suggestions if we want to change the partner's name (we'll see that later in the edit mode!),
2. A pencil that executes a predefined function if we click on it. If no function is specified, the ``url>`` method is taken.
 
Let's say we want to see the partner's record overview when we click on the pencil. In other words, we want to change the ``*ID`` in the URL to the partner's record.

Let's define a method ``url>`` that does just this in our ``+Person`` class model:

```
### DB ###
(class +Person +Entity)
(rel nm (+Need +Sn +Idx +String))      # Name
...
(rel txt (+String))                    # Info

(dm url> (Tab)
   (list "!person" '*ID This) )
```

``url>`` takes a ``(Tab)`` argument specifying which tab should be opened (which is obviously only interesting in an application with several tabs - not so much in our single page app).

You could see the output in the animated GIF above: when we click on the pen, the page jumps to display this record.


---------------------------

### Creating a table using ``+Chart`` and ``<table>``

Like already shown in the [To-Do App example](https://picolisp-blog.hashnode.dev/how-to-create-a-to-do-app-in-picolisp-desktop-version), we can use the ``+Chart`` in combination with ``<table>`` to display data in a grid together with some predefined action buttons.

First, we define the data used in the chart, which is the ``kids`` property of the current object. ``+Chart`` expects a numeric argument as number of columns. Additionally, we can call two functions: The first one gets called when the chart is filled with data; the second one gets called when the data is read.

```
(gui '(+E/R +Chart) '(kids : home obj) 7
   '((This) (list NIL This (: dat) (: pa) (: ma)))
   cadr )
```
What does that mean?
-  ``'((This) (list NIL This (: dat) (: pa) (: ma)))`` creates **a list of lists** out of the ``(kids : home obj`` data, i. e.  exactly one value per column and row.
- ``cadr`` is equivalent to the **[second item of a list](https://picolisp-blog.hashnode.dev/60-picolisp-functions-you-should-know-6-lists-and-strings)**, which corresponds to the actual child object. 

It might be a bit confusing, but it should clarify if you check the actual table output and its function.

-----------------------------------

After we defined the input value, we can define the table. The ``<table>`` function takes the argument "attributes", "title", "head" and "program". Let's set attributes and title to ``NIL`` and define the head as:

```
(<table> NIL NIL
   '(NIL (NIL "Children") (NIL "born") (NIL "Father") (NIL "Mother"))
```

Then we create our input fields just like specified above:

```
(do 6
   (<row> NIL 
      (choPerson 1)
      (gui 2 '(+Obj +TextField) '(nm +Person) 20)
      (gui 3 '(+Lock +DateField) 10)
      (gui 4 '(+ObjView +TextField) '(: nm) 20)
      (gui 5 '(+ObjView +TextField) '(: nm) 20)
      (gui 6 '(+DelRowButton))
      (gui 7 '(+BubbleButton)) ) )
   (<row> NIL NIL (scroll 6 T)) )
```

*Please ignore ``choPerson`` for the moment, we will come back to that in the next post.*


![table.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1634578900059/k3SS-HiYI.png)

-------------------------

### Output table finished!

Let's apply a little bit of formatting. PicoLisp has a built-in function ``<grid>`` that formats in rows and columns similar to a table. Each four "elements" will be placed in one row, and the width is determined by the longest item.

```
(<grid> 4
   "Occupation" (gui '(+E/R +TextField) '(job : home obj) 20)
   "Father" (gui '(+E/R +Obj +TextField) '(pa : home obj) '(nm +Man) 30)
   "born" (gui '(+E/R +DateField) '(dat : home obj) 10)
   "Mother" (gui '(+E/R +Obj +TextField) '(ma : home obj) '(nm +Woman) 30)
   ... )
```


![grid.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1634579425536/HcqWJBIcN.png)

------------------------

### Setting a default starting point

Lastly, let's define a default starting point for our application. This means that a default record should be loaded when we point the browser to http://localhost:8080.

How can we do that? First thing, we need to set the ``val`` of our ``*DB`` global variable, for example to record ``{A12}`` (Queen Elizabeth). In the REPL:

```
$ family-edit.l -family~main +
family: (set *DB '{A12})
-> {A12}
family: (show *DB)
{1} {A12}
   +Woman {3}
   +Person {2}
   +Man {4}
-> {1}
```

--------------

Next, let's load this value as default ``*ID`` at session startup. The session should also start immediately at loading the page.

```
(when (app)
   (redirect (baseHRef) *SesId *Url "?*ID=" (ht:Fmt (val *DB))) )
``` 

Now we can also change our html title dynamically to the person' name:

```
html 0 (; *ID nm) "@lib.css" NIL
```

With this, we get directly to Queen Elizabeth's entry when we visit http://localhost:8080.

-------------------------

With this we have all data of the record in a clear and user-friendly output format. However, the input fields are all disabled and we cannot save any changes.

Let's see how to change this in the next post.

-----------------------

You can find the source code up to this point [here](https://gitlab.com/picolisp-blog/web-applications/-/blob/main/database/family-edit-step1.l).

-------------------------

### Sources

- https://software-lab.de/doc/index.html   
- https://software-lab.de/doc/app.html