Web Application Programming in PicoLisp: Prefix Classes

Web Application Programming in PicoLisp: Prefix Classes

·

6 min read

In the last post, we have shown how to create an extremely minimalistic HTML form without any styling. This was the code:

(form NIL
   (gui 'a '(+TextField) 30 "First Name")                    
   (<br>)   
   (gui 'b '(+TextField) 30 "Last Name")                         
   (gui '(+Button) "Send" ) )

Today we will show how we can add styles and other things to our code using Prefix Classes.

Go here to view the hosted demo page.


What are Prefix Classes?

As explained in the Object-Oriented Programming posts, classes can inherit methods and properties of one or more other classes. This concept is called "multiple inheritance". If the same method or property appears several time within these classes, the first appearance is the valid one.

Technically, there is nothing special about prefix classes. They are just normal classes. They are called "prefix" because they are intended to be written before other classes in a class's or object's list of superclasses.

Prefix Classes can be used to add or modify characteristics that are defined within the GUI class. For example, the the prefix class +Mono displays text in monospace, while +Tinydisplays it in tiny font.


The +Style Prefix Class

By using the Style-prefix class, we can add CSS styles to our GUI classes. Let's modify our example so that it utilizes again the Bootstrap-classes from our previous example.

(action
   (html 0 "Simple Session" *Css NIL
      (<div> "d-flex flex-column"
         (<h1> "d-flex m-5 justify-content-center" "A little POST example with the GUI framework") )
      (<div> "d-flex flex-column bg-light mx-5"
         (<div> "d-flex justify-content-center my-5"

            (form NIL
               (gui 'a '(+Style +TextField) "mx-5" 30 "First Name")
               (<br>)
               (gui 'b '(+Style +TextField) "mx-5 my-1" 30 "Last Name")
               (gui '(+Button) "Send" ) ) ) ) )

Then it looks like this:

stateful_gui.png


Initializing: +Init and +Cue

To initialize a field with values, there are two possibilities:

  • the +Init class populates the field with a value. If the user clicks inside the field, the text will be preserved.

initclass.png

  • the +Cue class populates the field with the specified text inside angled brackets: <my text>. When the user clicks inside the field, it disappears. Note that the text only appears after sending the form, i. e. if a mandatory field has been left blank.

Cueclass.png


Enabling and Disabling: Able, Lock, (disable) and +Rid

Sometimes we need to enable or disable forms or single elements. The easiest way to do that is by using the Prefix Class +Lock, which doesn't take any further arguments:

(gui '(+Lock +Style +TextField) "mx-5 mb-2" 30 "Locked Textfield")                               
(gui '(+Lock +Style +Button) "mb-5" "Locked Button" )

+Lock.png

The user can't click on any of the fields, they are disabled.


The +Able class is more flexible, as it takes an executable argument and disables the field if the argument evaluates to NIL.

(gui '(+Able +Style +TextField) '(< 3 5) "mx-5 mb-2" 30 "Enabled Textfield")
(gui '(+Able +Style +Button) '(< 5 3) "mb-5" "Disabled Button" )

The text field is enabled because (< 3 5) evalues to T while the button is disabled because (< 5 3) evaluates to NIL.

ABLE.png


It's also possible to disable the form in total using the disable function, which takes an executable argument. If the argument evaluates to non-NIL`, all elements in the form are disabled **except** the ones with a prefix-class+Rid``:

(form NIL
   (disable (< 3 5))
      (gui '(+Init +Style +TextField) "Disabled textfield" "mx-5 mb-2" 30 "Enter your text: ")
      (gui '(+Rid +Init +Style +TextField) "Enabled by +Rid" "mx-5 mb-2" 30 "Enter your text: ") )

ridclass.png


The +JSclass

Let's take another look at last post's example, our button that prints a text field's content to standard error.

(form NIL
   (gui '(+Style +TextField) "mb-2" 30 "Enter your text: ")   
   (gui '(+Button) "Print" '(msg (val> (field -1))) )

When we press the button, the whole page is reloading. Now let's add another button with the same functionality, but prefix class +JS:

   (gui '(+JS +Button) "Print" '(msg (val> (field -1))) )`

jsprint.png

Now let's try both. When we press the button "Print", our page is reloaded and our text input is displayed on standard error. If the "+JS Print" button is pressed, our text input is also displayed on standard error, but without reloading! (The only exception is if there is no session running yet. In this case, the page will reload once to establish a session.)


How does it work? Comparing the HTML source code of both buttons, we can see that the +JS has an additional onclick="return doBtn(this)" tag and there is a form.js script sheet included, which causes the browser to only re-evaluate the GUI elements without reloading the full page.


Defining variables with +Var

Usually if we have a form sheet, we want to do something with the data. Say we want to write this data to a global variable called *TextVariable. When the button Print is pressed, this data should be written to standard output. Also, we want to print the content of the variable as normal text block.

In order to do all this, we will use the prefix class +Var, which takes a variable name as argument:

(setq *TextVariable "Initial Value")
(gui '(+Style +Var +TextField) "mb-2" '*TextVariable 30 "Enter your text: ")
(gui '(+Style +JS +Button) "mb-5" "+JS Print" '(msg (val> (field -1)))))
(<p> (prinl "*TextVariable: " *TextVariable)))))

The page is initialized with a text field and text block content "Initial Value".

textvar1.png

Now let's modify the value and press +JS Print. Immediately we see our new value in the standard error of the server process. However, the <p>-block remains unchanged, because the page has not been reloaded.

textavr1.png


Modifying values with +Val and +Set

We already heard that each gui object has a method val> to return the value of a property, and set> to set its value. These methods are inherited from the abstract top class +gui.

However, there might be situations where we want to customize these methods to our own needs. This can be done using the prefix classes +Val and +Set respectively. Both take a function as argument that overwrites the methods val> and set> respectively. Let's look at the following example:

(gui '(+Val +NumField) '((N) (* N N)) 10 "Enter a number: ")
(gui '(+Style +Button) "mb-3" "get square value" )

We have a number input field. However, the value of the field is not the content, but its square, because the value is multiplied with itself each time it is retrieved:

getsquare.png

If we type 10 and press the button, the field content becomes 100, if we press again, 10000.


The complement to +Val is +Set. It is called everytime a value is set to another value.

(gui '(+Set +NumField) '((N) (* N N)) 10 "Enter a number: ")
(gui '(+Style +Button) "mb-3" "get square value" )

Look and feel of that function are exactly identical to the +Val version, so what's the difference?

The difference is how the data is stored internally. Say we type 10 and press the button. In the +Val case, the actually stored value is 10, although the displayed value is 100. In the +Set case, the actually stored value is 100.


The source code of the examples can be found here.


These were some of the most important prefix classes. In the next post, we will take a look at some further GUI elements.


Sources: software-lab.de/doc/form/form.html
software-lab.de/doc/app.html