How to create a To-Do App in PicoLisp (Responsive Version) - Part 1
Tags: picolisp, webdev, lisp
Published: 2022-03-03 10:41:54

In the last post, we showed how to create a To-Do list using the ``+Chart`` classes of the PicoLisp GUI framework. It was very easy, but the layout is more or less fixed.

Today, we will create our own app which means that we need to write some functions from scratch. As a little teaser, this is a little demo of how the final result will look like:

![thingstodo.gif](https://cdn.hashnode.com/res/hashnode/image/upload/v1634053019553/cH_7arsTw.gif)

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

We will do it in two steps: First we define the logic, and in the second step the CSS.

### Task definition

So what do we actually want? Let's write a little app that displays a list of to-do items. The user can delete each of these items or add a new one. As a little extra-feature, it should be possible to customize the text color.

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

### Some preparation for set-up

As first step, let's create the stub. It's the same like in all previous projects:  we need to call ``(app)``, ``(action)``, and ``(html)``.

Instead of ``(app)`` let's use again the automatic redirection to a specific port at loading with ``(and (app) *Port% (redirect (baseHRef) *SesId *Url))``.


```
(setq *Css '("@lib.css" ))

(and (app) *Port% (redirect (baseHRef) *SesId *Url))

(action
   (html 0 "Todo" *Css NIL
      (<h1> "" "Things To Do") ) ) 
```

Then we start the server from the terminal by 

```
$ pil @lib/http.l @lib/xhtml.l @lib/form.l  --server 8082 +
```

You can now point the browser to http://localhost:8082/todo.l and should see an almost empty tab with title "Todo" and a header "Things To Do". 

![header.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1634054349875/uq2DGbHPo.png)


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

### Prepare a Dummy List

Next, let's start with a little dummy list to create some fake data. We can overtake the data structure from the last example, i. e. a list where each item is a list too. Each list item has the structure ``( <item description> <due date> <color> )``, for example like this:

```
(ifn *ToDoList
   (setq *ToDoList '(("shopping" 781423 "green")("cooking" 732842 "red"))) )
```

Next, we want to access each of these items one by one and print them out. This can be realized by a ``for``-loop:

```
(form NIL
   (for (I . Item) *ToDoList 
      (<p> NIL (printsp Item))
      (gui '(+Button) "Delete") )
```

When we put this now inside our ``html`` function and re-start the session, we should see something like this:


![thingstodo.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1634054551883/f2x2_SmSU.png)


Of course, the button doesn't have any function yet. Pushing it will simply cause a page reload.

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

### Writing the ``delete`` function

Basically, what we want to do is to delete the current ``Item`` list element from the ``*ToDoList`` once we press the button. Deleting items can be realized using the ``del`` function:

> ``(del 'any 'var ['flg]) -> lst``

> Deletes ``any`` from the list in the value of ``var``, and returns the remaining list. If ``flg`` is NIL and ``any`` is contained more than once in the value of ``var``, only the first occurrence is deleted.

So, basically we could try something like this:

```
   (for (I . Item) *ToDoList 
      ...
      (gui '(+Button) "Delete" '(del Item '*ToDoList) )
```
However, when we press it, nothing happens! Why?

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

In our ``for``-loop we are iterating through the list and keep ``Item`` as a temporary variable. However, after the build-up is finished, there is no link between ``Item`` and the actual list anymore. Therefore the interpreter doesn't "know" which ``Item`` we're referring to.

So we need to do it differently - for example, using the ``fill`` function: The ``fill`` function non-destructively fills a pattern ``any`` with its current value. The item to be substituted is marked with an ``@`` symbol. Like this:

```
(for (I . @Item) *ToDoList
   (<p> NIL (printsp @Item))
   (gui '(+Button) "Delete" 
      (fill 
         '(del '@Item '*ToDoList))) 
```

*Note: no quote ``'`` is needed prior to the ``fill``.*

Great, now the deletion works! However, when we expand our dummy list to expand it further, we notice that it seems a little bit inconsistent. For example, the last item cannot be deleted because an empty list is re-created due to the ``(ifn *ToDoList (...))`` statement on top of the program. However, when the full list returns, we need to press the "Reload" button to view it properly.

Again, what is happening?

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

When we ``POST`` our form by pressing on the button, all form elements are re-evaluated, but **not the complete web page**. Therefore some parts remain unchanged and we see inconsistencies. In order to force a full reload, we can use the ``url`` function which loads a given URL or filename. We want to reload our *current* page which we can refer to with the pre-defined global variable ``*Url``.

So, now we have **two** functions we want to execute with one button press: the ``del`` function and the ``url`` function. How can we connect them to one statement?

For this we can use the function ``prog``:

> (prog . prg) -> any 

> Executes ``prg``, and returns the result of the last expression. 

Applied to our button:

```
(gui '(+Button) "Delete"
   (fill
      '(prog
         (del '@Item '*ToDoList)
         (url *Url) ) ) )
```

Fine, now we can delete items very smoothly. Once the last item is deleted, the whole list re-appears.


![deletegif.gif](https://cdn.hashnode.com/res/hashnode/image/upload/v1634057469651/bbuwsYqH1.gif)

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

### Creating the "Adding items" form

So, we have a list now, and we can delete items. So there is only one task left - adding new ones.

Let's open a new ``form`` function and define the data input: a ``+TextField`` for the item description, a ``+DateField`` for the date, and a ``+RgbPicker`` for the color. Of course we also need a submit button.

Let's use the ``+Cue`` prefix class to pre-populate the field with some hints what kind of input is expected:

```
      (form NIL
         (gui '(+Cue +TextField) "Thing to do" 30 )
         (gui '(+Cue +DateField) "Enter a date" 10 )
         (gui '(+RgbPicker) )
         (gui '(+Button) "add item") )
```

The result looks like this:

![thingtodo.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1634057732407/gJMuJLlZN.png)

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

### Appending data

Now the last thing we need to do is to get the form items and add them to the list. We can add the input for each field by relative referencing. For example,

```
(val> (field -3))
```

returns the input value of the ``+TextField`` because it is located three form elements prior to the submit button. Once we get all items, we need to connect them to a list using the ``list`` function:

```
(list (val> (field -3)) (val> (field -2)) (val> field -1)))
```

We can append this to a given list using the function ``queue`` with following syntax: ``queue <list> <new item>``. To keep the code a little bit more readable, let's extract this to a new function ``addToList``:

```
(de addToList ( Item Date Color )
   (queue '*ToDoList (list Item Date Color )) )
```
and modify the button:

```
(gui '(+Button) "add item" '(addToList (val> (field -3)) (val> (field -2)) (val> field -1)))
```

Now it *almost* works. We only observe the same issue like before - the new items are only displayed once the full page is reloaded. So let's apply the same magic as before by connecting the functions with ``prog``:

```
         (gui '(+Button) "add item"
            '(prog      
               (addToList (val> (field -3)) (val> (field -2)) (val> (field -1)))
               (url *Url) ) ) 
```

And now it works!


![finalnostyling.gif](https://cdn.hashnode.com/res/hashnode/image/upload/v1634058465692/T_ZIg-1G6.gif)

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

The code for this example up to this point can be downloaded [here](https://gitlab.com/picolisp-blog/web-applications/-/blob/main/todo-list-example/todo-responsive-noCSS.l). The hosted version is [here](http://www.picolisp.com/blog/src/todo-responsive-noCSS.l).

It works now, but you couldn't really call it beautiful. In the next post, we will take care about the styling.

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

### Sources

- Icons made by <a href="https://www.freepik.com" title="Freepik">Freepik</a> from <a href="https://www.flaticon.com/" title="Flaticon">www.flaticon.com</a>