The SUBDIALOG clause

Purpose of SUBDIALOG

The SUBDIALOG clause defines a declarative dialog to be attached to the current procedural DIALOG block.

By using form inclusion (with the FORM clause in LAYOUT sections) and declarative dialogs + SUBDIALOG, you enforce code reusability in your application sources.

Defining the declarative dialog

The declarative dialog is implemented outside the scope of the using DIALOG block, at the same level as a function.

The declarative dialog can be defined in a different module, to be reused in other DIALOG instructions. The sub-dialog module must be imported with the IMPORT FGL instruction.

Like other module elements such as functions and reports, the name specification is mandatory when defining a declarative dialog. The name of the declarative dialog will be referenced in a SUBDIALOG clause of a procedural dialog instruction.

In the "comment.4gl" module:

DIALOG comment_input()
   ...
END DIALOG
In the using module (note that we use the module prefix here):
IMPORT FGL comment
...
FUNCTION mydialog()
   DIALOG ...
      ...
      SUBDIALOG comment.comment_input
      ...
   END DIALOG
END FUNCTION

See also Identifying sub-dialogs in DIALOG.

Sub-dialogs in form definitions

Implementing a sub-dialog as a declarative dialog in a separate module is typically used in conjunction with the FORM clause, in the LAYOUT section of form specification files:

LAYOUT
...
FORM "comment"
...
END

Semantics with SUBDIALOG

In terms of semantics, behavior and control block execution, a declarative dialog attached to a procedural dialog with SUBDIALOG, behaves like a sub-dialog that is defined inside the procedural DIALOG block.

For example, BEFORE INPUT inside an INPUT block of a declarative dialog will be executed when the focus goes to one of the fields of that sub-dialog.

Scope of dialog instructions

Other sub-dialogs can reference the attached declarative dialog in the current scope.

For example, to execute a NEXT FIELD instruction referencing a field in another sub-dialog:
DIALOG ... -- Parent dialog block
   ...
   NEXT FIELD the_comment  -- Field of the declarative dialog.
   ...
END DIALOG

Scope of DIALOG keyword

When using the DIALOG keyword inside a declarative dialog block to use ui.Dialog class methods, it references the current procedural dialog object:
DIALOG comment_input()
   ...
   CALL DIALOG.setFieldActive("the_comment",TRUE)
   ...
END DIALOG

Writing generic code

To be reused by different procedural DIALOG instructions, the code of sub-dialog modules must be generic. However, if the sub-dialog code needs to interact with the parent DIALOG, it must be possible to call a function from the parent DIALOG.

You achieve this by using function references. Parent modules can then configure the sub-dialog module at runtime, with callback functions:

  1. Create a user-defined TYPE with the FUNCTION type matching the callback function of the using module.
  2. Define a module variable, with the declared function type. If you want to keep it private to the module, define a setter function to assign the variable with the callback function reference.
  3. When the parent DIALOG needs to be notified by some change in the sub-dialog, check that the callback variable is not NULL, and call the function with appropriate values.
Tip: You do not have to implement a lot of complex callback functions. The main purpose is to indicate to the parent DIALOG, that something happened in the sub-dialog. The parent DIALOG can then query the sub-dialog module for more information, as long as the sub-dialog module provides functions to query its status.
PUBLIC TYPE cb_comment_event FUNCTION (event STRING)
PRIVATE DEFINE cb_ce cb_comment_event
...
PUBLIC FUNCTION set_event_callback(f cb_comment_event)
    LET cb_ce = f
END FUNCTION

DIALOG comment_input()
    ...
    IF cb_ce IS NOT NULL THEN
       CALL cb_ce("comment_changed")
    END IF
    ...
END DIALOG

For a complete example, see Example 3: DIALOG with SUBDIALOG.