Rich UI validation and formatting

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:
  • The error message that results from a failed view-level validation is always displayed in a tooltip near the widget. When the widget loses focus, the tooltip is hidden, and when the widget regains focus, the tooltip is shown.

    The behavior just described is a characteristic of Dojo.

  • When the user moves the focus away from the widget, controller-level validation occurs; but only if the view-specific validation was successful.
As invoked by your code or by the isValid function, the validate function does as follows:
  1. 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.
  2. 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.
  3. 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.
  4. 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.
  5. 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:
    • If any of the elementary validations fail, control returns to the user. If the controller is in a form, a message associated with the first failed validation is stored.

      You can accept the default EGL message for a given validation; but if you want to specify your own message, review the description in “Use of properties files for displayable text.”

    • If all the elementary validations are fulfilled, the validators run, as described next.
  6. 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:
  • Set the controller property validStateSetter, which identifies a function for the EGL runtime to invoke at the end of validation.
  • Create the function, which has two parameters and no return value. The first parameter receives the widget that holds the data being validated. The second parameter indicates whether the validation succeeded. Here is the Delegate part to which each function must conform:
    Delegate MVCValidStateSetter(widget Widget in, valid boolean in) end
  • In that function, assign a different or additional class (or a different CSS ID) to the widget that has invalid content or, more likely, to the label of that widget.

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:
  1. 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.
  2. 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.
  3. 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.
  4. 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:
  1. 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.
  2. 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.
  3. 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.
  4. 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.