Skip to content

Lesson 4: Client side TODO list

vcgalpin edited this page Dec 18, 2020 · 2 revisions
fun remove(item, items) {
  switch (items) {
     case []    -> []
     case x::xs -> if (item == x) xs
                   else x::remove(item, xs)
  }
}


fun todo(items) {
   <html>
    <body>
     <form l:onsubmit="{replaceDocument(todo(item::items))}">
       <input l:name="item"/>
       <button type="submit">Add item</button>
     </form>
     <table>
      {for (item <- items)
        <tr><td>{stringToXml(item)}</td>
            <td><form l:onsubmit="{replaceDocument(todo(remove(item,items)))}">
                 <button type="submit">Completed</button>
                </form>
            </td>
        </tr>}
      </table>
     </body>
   </html>
}

fun mainPage(_) {
  page
   <#>{todo(["add items to todo list"])}</#>
}

fun main () {
 addRoute("",mainPage);
 servePages()
}

main()

This program creates an interactive, client-side TODO list. It works on the same principle as the previous one: values are submitted using a form, and the form response is an action performed on the client side (using l:onsubmit).

The todo function takes as an argument the current list of items. When first called, this list has one element, "add itens to todo list". The function creates an XML snippet containing two things: a form for adding todo list items, and the todo list itself. When submitted, the form replaces the document content (using replaceDocument) with the result of calling todo again with an extended todo list.

The todo list itself is rendered as a table, with the first column containing the todo list items themselves and the second column containing buttons which, when pressed, will remove the list item. The buttons are embedded in forms, again using the l:onsubmit action so that the action will be performed on the client side without an intervening POST action.

The todo list is built using a comprehension of the form for (item <- items) <XML snippet>. There are a couple of things worth mentioning about this construct. Comprehensions allow us to iterate over the elements of a list, such as items, and on each iteration the corresponding element is bound to a variable item, which can be used in the body of the comprehension. The body of the comprehension in this case is the XML snippet that constructs each table row. In general, the result of a comprehension is a list constructed by concatenating all of the lists constructed in each iteration; thus, the return value of a comprehension body has to be a list. This is the case because in Links, the type Xml is an alias for the list type [XmlItem], that is, Xml content is a list of XmlItem values, each of which corresponds to a single XML tree.

(For somewhat obscure reasons, this works fine as long as we are returning only a single XML tree as the result of for. But if we wanted to return multiple XML items, we could enclose them in the XML quasiquote tags <#>...</#> so that Links will parse all of them as a single list.)

Finally, the remove function traverses the todo list and removes the item(s) with matching names.

Exercises

  1. What happens if you stop the Links interpreter and restart it? Is the todo list persistent?

No, it is reset.

  1. What happens if you visit the todo list from two different browser windows? Is the todo list shared across them?

No, the different windows have independent todo lists. We will show how to create a shared, persistent list in a later lesson.

  1. What happens if you add multiple identical items to the todo list? What happens if you try to remove one of them? How could we change this behavior?

Adding multiple items results in a todo list with repeated elements. If one of them is deleted, the first occurrence of that item is removed (regardless of whether it was the item selected). The remove function can be modified to remove every occurrence of an item but this is not a desirable solution either. Maybe the remove function could remove items based on their position, and the remove buttons could use item positions instead of their names when calling remove.

  1. Links's comprehension syntax allows for where clauses, as follows:

    for (x <- [1,2,3]) where (x == 2) [x+10]
    

    evaluates to [12]. Can you use comprehensions to rewrite remove?