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:
Note that all static properties are determined by the UI definition, and none by the application program.
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.
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" vSee 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 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_typei.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.