This topic briefly describes the Model, View, and Controller (MVC)
way of organizing logic and then explores how Rich UI uses the idea
of MVC to support validation and formatting.
MVC
Modern data-processing systems separate
the
view from the
model, and those terms are variously
defined:
- The view is the user interface, or the logic that supports
the user interface, or the business data in the user interface
- The model is a database (or other data storage), or the
logic that accesses a database, or the data that is sent to or retrieved
from a database
The controller is the logic that oversees the transfer
of data between the user interface and the database-access logic.
In
many cases, the acronym MVC refers to processing across multiple platforms.
For example, a Rich UI application on a Windows™ platform might be considered to be
the view (and to include the controller), while a service that accesses
a database might be considered to be the model.
We can also
consider a division of view from model in the Rich UI application
itself. In this case, the terms have the following meaning:
- The view is a widget in the user interface. Data that is
placed into that widget must be validated before it can be used in
other processing. Application data that is intended for display by
the widget must be formatted before display.
- The model is a data field that is internal to the application.
An EGL definition, the controller, lets you tie a specific
view—a specific widget—to a specific model. The controller also oversees
validations and format rules, as described later.
You can also
define a form that identifies a set of display fields and the related
controllers. In such a form, you can display the error messages that
result from validations and formatting.
The next sections describe
the Controller and how to work with it. For details on creating forms,
see Form processing with Rich UI .
The Controller in Rich UI
Consider the following
declarations:
nameField TextField;
name String {inputRequired=yes,
validationPropertiesLibrary=myLibrary,
inputRequiredMsgKey="requiredMessage"};
The
declaration of
name has the following annotations:
- inputRequired ensures the user will
provide input to any widget (a view) that is associated with the data
field (a model)
- validationPropertiesLibrary identifies
the library (stereotype RUIPropertiesLibrary) that
includes declarations for use in messages and other text. If you do
not include this annotation, the EGL Runtime accesses default messages.
For details on customizing validation messages in Rich UI, see “Use
of properties files for displayable text.”
- inputRequiredMsgKey identifies a customized
validation message
Here is an example declaration of a Rich UI controller:
nameController Controller
{ @MVC
{ model=name, view=nameField },
validators = [myValidationFunction01, myValidationFunction02]
};
The declaration ties a specific model—the name field—to
a specific view—the nameField widget. In general,
only a widget of a special type can be a controller view, as noted
in “Widgets that can be used as controller views.”
You control
the transfer of data between the model and the view, as follows:
- To transfer data from the view to the model, you validate the
data and then commit the validated data. In most cases, the commit
step removes formatting characters such as currency symbols.
- To transfer data from the model to the view, you publish the data.
In most cases, the publish step formats the data for display.
In our example, the controller declaration also lists
a set of
validators, which are functions that you write and
that validate input in sequence, one function after the next. Each
validator is based on the following Delegate:
Delegate
MVCValidatorFunction(input String in) returns(String?)
end
Input to a validator is considered to be valid
if the function returns an empty string or null, but not if the validator
returns a string with content. Such a string is considered an error
message. If a function returns an error message, the subsequent functions
do not run.
In most cases, you commit data from the view to
the model only after validating the data. Here is the syntax for committing
the user input from the
nameField widget into the
name field:
if (nameController.isValid())
nameController.commit();
end
Validating the user input
Controller-level
validation is a value check that is caused by the invocation
of either of two controller-specific functions:
- The isValid function is invoked whenever
the user moves the focus away from the widget. Also, your code can
invoke the function, which displays error messages that result from
a failed validation.
An error message is removed only when a successful
validation occurs, not when the user moves the focus back to the widget.
- The validate function is invoked by
the isValid function and stores a message
for the first-found error, but does not itself display the message.
You can access the stored error message by invoking the controller-specific getErrorMessage function,
which has no parameters and returns a value of type STRING?.
Your
code can invoke the validate function instead
of the isValid function, for greater control.
For example, your code might invoke the validate function
to test whether to display a help page or to take some other action
if an error is found, and you can avoid displaying an error message
returned from the function.
A related controller-specific function is isValidStateInitialized,
which specifies if a controller-specific validation function (whether isValid or validate)
ran at least once in the current execution of the Rich UI application.
The function isValidStateInitialized has
no parameters and returns a Boolean.
Incidentally, several types
of EGL Dojo widgets support a
view-level validation,
which is a value check that occurs after each user keystroke. The
view-level validation is available in the following types of widgets:
- DojoCurrencyTextBox
- DojoDateTextBox
- DojoTextField
- DojoTimeTextBox
For widgets of those types, the following rules apply:
As invoked by your code or by the
isValid function,
the
validate function does as follows:
- Runs the function referenced by the retrieveViewHelper Controller
field (not shown in our example), which identifies the function that
retrieves the widget content. The function has no parameters and returns
a string. You might use this function to convert the input in some
way. However, you do not need to set the retrieveViewHelper field
in most cases. In the absence of that field, the widget content is
available as a string, for subsequent processing.
- Runs the functions referenced by the unformatters Controller
field (not shown in our example), which identifies an array of functions.
Each has a string parameter (STRING in) and returns a string. You
might use these functions to remove formatting characters from the
input. The first-listed function accepts the string returned from
the retrieveViewHelper function, and each
of the later-listed functions accepts the string returned from the
previous function. You might not need to set the unformatters field.
- Removes the formatting characters from the user input in accordance
with the formatting field, if any, that you specified in the source
code. An example of a formatting character is the currency symbol,
which is associated with the currency field.
A second example is the separators specified by the dateFormat field.
- Returns control to the user if a validation error occurs related
to data type; for example, the user might have typed a character when
the model is a numeric field.
- Runs the elementary validations, as specified by EGL fields that
are set on the model, not on the view.
A later section lists
the available validation fields, which include inputRequired,
as shown in our example. (That field ensures the user will provide
input to any widget that is associated with that the data field.)
Here
are the possible outcomes of the elementary validations:
- Runs the validators in the order specified in the controller validators array.
Each validator accepts the input string (STRING in) without formatting
characters such as the currency symbol; and each validator returns
a string or a null. (The return value is of type STRING?.)
If the
validator returns a null or blank, the EGL Runtime considers the validation
to have succeeded. However, the following statements apply if the
validator returns a different value—for example, a value retrieved
from a properties file, as described in “Use of properties files for
displayable text”:
- The EGL Runtime considers the validation to have failed.
- Control immediately returns to the user; in that case, the subsequent
validators do not run.
- The returned string is stored as an error message. For details
that are specific to the EGL Dojo widgets that support view-level
validation, see Displaying a controller-level error message in an EGL Dojo widget.
You can write a validator that is accessed by multiple
controllers. The reuse is possible because you can now identify the
controller, as shown here:
function commonValidator(input string in) returns(string?)
currController Controller = MVCLib.getCurrentContext();
viewId string = currController.view.id;
end
Last, the publishMessageHelper field
takes a function that has a single parameter, which is modified by
IN, and no return value. This function is invoked when the widget
gains focus, and the input to that function is the last message that
was stored when the widget lost focus.
Changing the display of invalid input and handling
error messages
By default, a widget with invalid content
is displayed with a style specified in a Cascading Stylesheet (CSS)
class; specifically, the initial class such as EglRuiTextField, along
with the following, secondary class: FormErrorEditor. A web designer
in your organization is likely to set up the style sheet for best
effect.
You can assign a different set of CSS classes (or a
different CSS ID) in response to validation failure; or you can change
another aspect of the displayed output. For example, you can assign
CSS classes to a label, as occurs (by default) if you use a validating
form. (For details, see “Form processing with Rich UI.”)
Here
is the procedure for changing the displayed output after the controller-level
validation:
In the same function, you can access controller-level
error messages by invoking the controller-specific getErrorMessage function.
As noted earlier, that function has no parameters and returns a value
of type STRING?.
Displaying a controller-level error
message in an EGL Dojo widget
You have choices as to where
to place the error message that results from a controller-level validation:
- In relation to the EGL Dojo widgets that support view-level validation,
you can place the message in the tooltip where any message from a
view-level validation is displayed. In this case, the widget immediately
displays an error indicator in response to the error, but the controller-level
message is displayed only after the widget gains focus. (For the earlier
description of view-level validation, see Validating the user input.)
If the widget is in a form,
you can prevent the controller-level error message from being displayed
in an error label by removing the error label.
- In relation to any EGL or EGL Dojo widget, you can place the message
in an error label, as described in “Form processing with Rich UI.”
In this case, the widget immediately displays an error indicator in
response to the error, and the message is displayed as soon as the
error occurs.
If you decide to place the message in an error label
and are using an EGL Dojo widget that supports view-level validation,
you can prevent the message from being displayed in the tooltip. To
prevent the duplicate display, set the controller-specific publishMessageHelper field
to null or assign your own function to that field. If you do not set
the field or assign your own function, an internal EGL function is
invoked to direct the error message to the tooltip.
As noted in the documentation for the EGL Dojo widgets
that support view-level validation, you can set an error indicator
by invoking the widget-specific showErrorIndicator function
and can display an error message by invoking the widget-specific showErrorMessage function.
Committing the validated input
When you
run the controller-specific
commit function, data is transferred
from the view to the model. During that process, several functions
are invoked, as determined by a set of controller fields that give
you many options. Here are the fields:
- The retrieveViewHelper field identifies
the function that retrieves the widget content. The function has no
parameters and returns a string. You might use this function to convert
the input in some way. However, you do not need to set the retrieveViewHelper field
in most cases. In the absence of that field, the widget content is
available as a string, for subsequent processing.
- The unformatters field identifies an
array of functions. Each has a string parameter (STRING in) and returns
a string. You might use these functions to remove formatting characters
from the input. The first-listed function accepts the string returned
from the retrieveViewHelper function, and
each of the later-listed functions accepts the string returned from
the previous function. You might not need to set the unformatters field.
- Formatting characters are removed from the user input in accordance
with the formatting fields, if any, that you specified in the source
code. An example of a formatting character is the currency symbol,
which is associated with the currency field.
A second example are the separators specified by the dateFormat field.
- The commitHelper field identifies the
function that assigns a value to the model. The function takes a string
parameter and no return value. You might not need to set this field.
In its absence, the model receives the string that was provided by
an earlier function, if any, and that no longer includes formatting
characters.
Publishing the model data
When you run the
controller-specific
publish function, data is transferred
from the model to the view. During that process, several functions
are invoked, as determined by a set of controller fields that give
you many options. Here are the fields:
- The retrieveModelHelper field identifies
the function that retrieves the data content. The function has no
parameters and returns a string. You might use this function to convert
the output in some way. However, you do not need to set the retrieveModelHelper field
in most cases. In the absence of that field, the model content is
made available as a string, for subsequent processing.
- Formatting characters are added to the data content in accordance
with the formatting fields, if any, that you specified in the source
code. An example of a formatting character is the currency symbol,
which is associated with the currency field.
A second example are the separators specified by the dateFormat field.
- The formatters field identifies an array
of functions. Each has a string parameter (STRING in) and returns
a string. You might use these functions to format the output. The
first-listed function accepts the string returned from the retrieveModelHelper function,
with any formatting characters added in step 2. Each of the later-listed
functions accepts the string returned from the previous function.
You might not need to set the formatters field.
- The publishHelper field identifies the
function that assigns a value to the view. The function has a string
parameter (STRING in) and no return value. You might not need to set
this field. In its absence, the view receives the string that was
provided by an earlier function, if any, and that includes formatting
characters.
Validation and formatting fields
Each of
the fields used in Rich UI validation and formatting can be categorized
with a single letter:
- F is for formatting. An annotation in this category removes formatting
characters during commit and adds formatting characters during publish.
Any of these annotations can result in the display of an error message;
for example, if an input date is significantly different from the
required date format, or if an integer value is a number other than
0 or 1 but is associated with isBoolean.
- V is for input validation.
Here are all the fields:
- currency (F)
- currencySymbol (F)
- dateFormat (F)
- fillCharacter (F)
- inputRequired (V)
- inputRequiredMsgKey (V)
- isBoolean (F)
- isDecimalDigit (V)
- isHexDigit (V)
- lowercase (F)
- minimumInput (V)
- minimumInputMsgKey (V)
- numericSeparator (F)
- sign (F)
- timeFormat (F)
- timestampFormat (F)
- typeChkMsgKey (V)
- uppercase (F)
- validValues (V)
- validValuesMsgKey (V)
- zeroFormat (F)
You can specify the validation and formatting annotations
on DataItem definitions, on variables, and on Record fields; but they
are in effect for a given Rich UI controller only if you specify the MVC annotation
when declaring the controller.