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,”
which uses the mechanisms described here.
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 properties:
- 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 property, 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 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.
- You can call the isControllerValid function
to inspect the valid state of the controller without notifying the
valid state change listener.
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 property
(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 property
in most cases. In the absence of that property, the widget content
is available as a string, for subsequent processing.
- Runs the functions referenced by the unformatters property
(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 property.
- Removes the formatting characters from the user input in accordance
with that formatting properties, 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 property.
A second example are the separators specified by the dateFormat property.
- Returns control to the user if a validation error occurs related
to data type; for example, the user may have typed a character when
the model is a numeric field.
- Runs the elementary validations, as specified by EGL properties
that are set on the model, not on the view.
A later section
lists the available properties, which include inputRequired,
as shown in our example. (That property 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 property
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 property
to null or assign your own function to that property. If you do not
set the property 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 properties that
give you many options. Here are the properties:
- The retrieveViewHelper property 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 property
in most cases. In the absence of that property, the widget content
is available as a string, for subsequent processing.
- The unformatters property 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 property.
- Formatting characters are removed from the user input in accordance
with the formatting properties, 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 property.
A second example are the separators specified by the dateFormat property.
- The commitHelper property 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 property. 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 properties that
give you many options. Here are the properties:
- The retrieveModelHelper property 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 property
in most cases. In the absence of that property, 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 properties, 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 property.
A second example are the separators specified by the dateFormat property.
- The formatters property 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 property.
- The publishHelper property 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 property. 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 properties
Each
of the field-level properties used in Rich UI is described in “Form
field properties.” The current section simply lists the properties,
each of which is categorized with a single letter:
- F is for formatting. A property in this category removes formatting
characters during commit and adds formatting characters during publish.
Any of these properties 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 properties:
- 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)
The following properties are further described in “Use
of properties files for displayable text”:
- inputRequiredMsgKey (V)
- minimumInputMsgKey (V)
- typeChkMsgKey (V)
- validValuesMsgKey (V)
You can specify the validation and formatting properties
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 property
when declaring the controller.