Plasma GitLab Archive
Projects Blog Knowledge


[UP]
Reference
 Dialogs
 Data types
 Events
 Templates
 Session management and security
 Internationalization
 Output encodings
 Processing instructions
 The UI language
 The standard UI library
 WDialog API (O'Caml)
 Runtime models
   
Dialogs

Unsurprisingly, dialogs are the central notion of WDialog. The idea is that consecutive user interactions share the same state, one only being a small modification of the other. Imagine you want to program a table editor, and the user interface allows the user to add rows, add columns, delete rows, delete columns, and fill values into the cells of the table. All these actions are very similar, and the most important part of the shared state is the table the operations modify. So in our first approximation the dialog consists of a series of similar actions targeting to modify the same operand.

If we look closer at this model, we can find out that the "shared state" and the "interactions" have a certain structure. The state consists often of some "main state" and some "auxiliary state". In our example, the representation of the edited table would be "main state", but we can easily imagine that there are some variables around it that are only needed for certain actions. The operation "delete row" needs certainly a designator that determines which rows are object of the deletion. This could be some checkboxes, and it is possible to select the rows to delete, or it could be an input box for the index of the row to delete. Such a designator would be an example for "auxiliary state", typically it is only required for certain operations, and only for the next operation the user triggers.

The interactions seem to consist of an "output part" and an "input part". In general, the output part is the HTML page that is generated. The input part are the input boxes, checkbuttons, hyperlinks etc. the user can interact with, and that produce some value that is transferred to the server. Unfortunately, the output part and the input part are mixed up on the HTML level: If I want to create a text box, I have to send a text like

<input type=text name="name_of_the_box" value="initial_value">
to the browser, i.e. if I want to create the input facility, I have to output text first (and it must be embedded at the right place). Furthermore, I have to remember the name of the text box, so I can later identify the input value the browser sends back when the user has filled out the text box.

It is easy to imagine that it can become quite difficult to manage the various components of the state, and that it is a complicated task to generate the HTML code for input widgets such that the data stream returned by the browser can be interpreted. WDialog even goes one step further: It integrates the concepts for state and the concepts for input widgets.

An input box needs a state variable that is bound to it. Such a variable can be declared by the XML statement:

<ui:variable name="textvariable" type="string"/>
The state management routines of WDialog automatically ensure that the state variables keep their values in the whole dialog cycle (i.e. in the current series of user clicks). This special property is called the persistency of dialog state. This does not mean that the dialog state is stored in some non-volatile memory, it only means that the state survives the cycles of the underlying HTTP protocol.

Furthermore, the creation of the input box is denoted by the special XML element

<ui:text variable="textvariable"/>
which is automatically translated by WDialog to the previously mentioned <input type=text> HTML element, but which should be better considered as a new widget with unique properties. This text box always visualizes the current value of the variable textvariable. This means that if the program modifies the variable, the text box will reflect the change at the next opportunity, and it means that if the user edits the text box, the updated value will be stored in the variable.

In WDialog all input widgets are tied to state variables. There is even a symmetry between the possible input widgets and the data types of variables. Obviously, a text input box needs a string variable. A selection box needs an enumerator variable. When I designed WDialog I extended the possible data types until I found a set that can easily represent the states of the widgets.

A dialog consists often only of one major "screen", i.e. one way of arranging the HTML page. In the "table" example, the main layout is certainly the table itself. However, it is also clear that there must be auxiliary pages (e.g. output a warning before continuing an action) such that it is possible to switch between the main page and the auxiliary pages without losing the state. Because of this, the dialogs may define several pages, i.e. fundamental ways of generating the HTML visualization at the current step of the dialog. A page simply consists of a tree of HTML elements and elements of the UI language like <ui:text> that are transformed to HTML elements. When WDialog selects the page, it selects between several of such trees of HTML visualization.

There is another aspect of pages. When the user clicks at a hyperlink or presses a button, an action is triggered, and a new page is displayed. The name of the action is called event.

The static properties of dialogs

The static properties are configured in the UI definition. The following elements have impact on this configuration:

  • ui:enumeration and ui:enum defines types that can be used for enumerator variables. The name of the ui:enumeration can occur in the type attribute of ui:variable declarations of the same dialog. See the description of ui:enumeration and ui:enum for more explanations, and for an example.

  • ui:variable declares the instance variables of the dialogs. These variables are persistent, i.e. they do not lose their contents between HTTP invocations. The variables are typed, see the chapter about Data types for a description of the (very simple) type system. ui:variable allows it to specify the initial value that is assigned to the variable when the dialog object is created. The variables can be accessed from the UI definition by the element ui:dynamic, and by all elements that have a variable attribute (i.e. ui:checkbox, ui:radio, ui:text and ui:password, ui:select, ui:textarea, ui:ifvar, ui:iterate, and ui:enumerate). The bracket expressions can refer to variables, too. The UI definition does not allow any algorithmic modifications of variables; the contents of variables can only be changed by user interaction. However, the application program can both read from and write to variables, see the language- specific sections Data types (O'Caml), and Data types (Perl) for an overview.

  • ui:page defines the HTML trees that can be output as visualization of the dialog. The pages can also be regarded as top-level templates, and most of the rules explained in the chapter about Templates can actually be applied to pages, too. Templates and pages only differ in the way they are called: While templates are explicitly invoked by UI definition code (ui:use), pages are called by the output routine of the dialog.

  • ui:context allows one to set context parameters for the scope of the dialog. See the chapter about Templates for details.

  • Another property of dialogs is the ability to generate events on certain user interactions. There must be a ui:form element in the page definition in order to enable this feature, and all interactors that can trigger events must only occur inside this ui:form. These interactors are ui:a, ui:button, ui:imagebutton, and ui:richbutton. There is a chapter about Events explaining the details.

Note that all static properties are determined by the UI definition, and none by the application program.

Programming dialogs

Once defined in the UI language, the dialogs exist for WDialog. However, there are not yet any actions that can be triggered by the events, and there are not yet any ways to set up the state variables such that the page can be displayed properly. This must be done by programming a Caml class (or Perl class, but the following text assumes Caml) that extends the base class dialog. The base class implements the fundamental behaviour of dialogs, but it is incomplete and must be complemented by two very special methods.

The first method has the fixed name prepare_page, and it is called just before a page is displayed. The task of this method is, in general, to set the state variables to the right values for the page generation. Remember that the generated HTML code usually refers to variables because of bound interactors, and that the code contains the values of these variable in one or the other way. For example, the variables could be loaded from a database.

The other method has the fixed name handle, and it is called just after the user has triggered an event. This method should find out the right action for the event, and perform this action. For example, it could save the user-modified contents of the state variables into the database. Another task of the handle method is to determine what happens next. The question is which page is to be displayed next (such that prepare_page can be called in turn, and that the page can be generated). It is also possible that handle decides to leave the whole dialog, and to continue with another dialog.

The dialog cycle consists of a possibly infinite sequence of prepare_page and handle calls.

The programmed class must inherit from the base class Wd_dialog.dialog with the type dialog_type as defined in the module Wd_types:

class my_dialog universe name env =
  object(self)
    inherit Wd_dialog.dialog universe name env
    
    method prepare_page() = ...

    method handle() = ...
  end
;;
The base class provides methods to access the variables. For example, it is possible read the value of a variable by

let v = self # variable "varname"
and you can set the variable to a new value by

self # set_variable "varname" v
See the chapter about Data types (O'Caml) for more such methods. The definition of dialog_type, found in Wd_types, can be used as reference.

After the dialog class has been defined, it must be registered in the universe. The universe contains which Caml class implements which dialog. In the simplest case, the registration can be done as callback from Wd_run_cgi.run:

Wd_run_cgi.run
  ~reg:(fun universe ->
         universe # register "my_dialog" (new my_dialog)
       )
  ()
The idea of the universe is explained below.
The dynamic properties of dialogs

After dialog objects have been created, the following dynamic properties can be expected:

  • The current page of the dialog is the name of one of the declared pages (initially the start-page). During the handle callback there is also the next page that is normally the same as the current page, but can be set to a different page by the callback implementation. Of course, the system will switch to the next page as the new current page when the next dialog cycle will be performed.

  • The values of the variables can be changed by interactors like ui:text and ui:password that are bound to variables, and by explicitly calling the set_variable method from the application program.

  • The name of the most recent event is automatically stored in the dialog object, and can be accessed by calling the event method.

  • The dynamic properties of dialogs can be extended by defining the two callback methods prepare_page and handle. The prepare_page callback should set variables that are needed to display the next page, and handle should look at the last event and should do the actions implementing the intended meaning of the events. The callback routines can access all static and all dynamic properties of the dialog.

The universe of dialogs

The remaining question is: how are dialog objects created, and what can the application programmer do to modifiy their behaviour? WDialog has the concept of a universe that defines what can be dynamically created, and the universe especially contains the registry of dialog object constructors. Such a constructor is simply a function that returns the new dialog object as result. In particular, constructors have the functional type

universe_type -> string -> environment -> dialog_type
i.e. the three arguments universe, dialog name (the string), and the environment are passed to the constructor, and the object of type dialog_type is returned. Because of this signature, implementations of dialog classes usually take exactly these arguments as class arguments (i.e. class my_dialog universe name env = ...), such that the new operator can be directly used to create the object (i.e. new my_dialog is a working constructor).

At program startup, the universe is initialized, and the application program (usually) puts the constructors for all dialogs into the universe. This is done by the register method of the universe (e.g. universe # register "my_dialog" (new my_dialog)). Later, the universe is used to create new dialog objects, e.g.

let dlg = universe # create env "my_dialog"
The new object dlg knows all static properties that have been declared in the UI definition, but it still in the initial state regarding the dynamic properties. When the application program creates the new object, it has to ensure that the variables are set up as required by the program logic.

There is another, internal usage of create. This method is also used by WDialog to reactivate the current dialog after process startup. The variables, and the other dynamic properties are set to the state they had when the last cycle ended, in order to keep the illusion that the dialog state persists across consecutive cycles.

This web site is published by Informatikbüro Gerd Stolpmann
Powered by Caml