7 Validate and Convert Input

Oracle JET includes validators and converters on a number of Oracle JET editable elements, including oj-combobox, oj-input*, and oj-text-area. You can use them as is or customize them for validating and converting input in your Oracle JET app.

Some editable elements such as oj-checkboxset, oj-radioset, and oj-select have a simple attribute for required values that implicitly creates a built-in validator.

Note:

The oj-input* mentioned above refers to the family of input components such as oj-input-date-time, oj-input-text, and oj-input-password, among others.

About Oracle JET Validators and Converters

Oracle JET provides converter classes that convert user input strings into the data type expected by the app and validator classes that enforce a validation rule on those input strings.

For example, you can use Oracle JET's IntlDateTimeConverter to convert a user-entered date to a date-only ISO string and then use DateTimeRangeValidator to validate that input against a specified date range. If the converters or validators included in Oracle JET are not sufficient for your app, you can create custom converters or validators.

About Validators

All Oracle JET editable elements support a value attribute and provide UI elements that allow the user to enter or choose a value. These elements also support other attributes that you can set to instruct the element how to validate its value.

An editable element may implicitly create a built-in converter and/or built-in validators for its normal functioning when certain attributes are set. For example, editable elements that support a required property create the required validator implicitly when the property is set to true. Other elements like oj-input-date, oj-input-date-time, and oj-input-time create a datetime converter to implement its basic functionality.

About the Oracle JET Validators

The following table describes the Oracle JET validators and provides links to the API documentation:

Validator Description Link to API Module

DateTimeRangeValidator

Validates that the input date is between two dates, between two times, or within two date and time ranges

DateTimeRangeValidator

ojvalidation-datetimerange

DateRestrictionValidator

Validates that the input date is not a restricted date

DateRestrictionValidator

ojvalidation-daterestriction

LengthValidator

Validates that an input string is within a specified length

LengthValidator

ojvalidation-length

NumberRangeValidator

Validates that an input number is within a specified range

NumberRangeValidator

ojvalidation-numberrange

RegExpValidator

Validates that the regular expression matches a specified pattern

RegExpValidator

ojvalidation-regexp

RequiredValidator

Validates that a required entry exists

RequiredValidator

ojvalidation-required

About Oracle JET Component Validation Attributes

The attributes that a component supports are part of its API, and the following validation specific attributes apply to most editable elements.

Element Attribute Description

converter

When specified, the converter instance is used over any internal converter the element might create. On elements such as oj-input-text, you may need to specify this attribute if the value must be processed to and from a number or a date value.

countBy

When specified on LengthValidator, countBy enables you to change the validator’s default counting behavior. By default, this property is set to codeUnit, which uses JavaScript's String length property to count a UTF-16 surrogate pair as length === 2. Set this to codePoint to count surrogate pairs as length ===1.

max

When specified on an Oracle JET element like oj-input-date or oj-input-number, the element creates an implicit range validator.

min

When specified on an Oracle JET element like oj-input-date or oj-input-number, the component creates an implicit range validator.

required

When specified on an Oracle JET element, the element creates an implicit required validator.

validators

When specified, the element uses these validators along with the implicit validators to validate the UI value. Can be implemented with Validators or AsyncValidators to validate the user input on the server asynchronously.

Some editable elements do not support specific validation attributes as they might be irrelevant to its intrinsic functioning. For example, oj-radioset and oj-checkboxset do not support a converter attribute since there is nothing for the converter to convert. For an exact list of attributes and how to use them, refer to the Attributes section in the element’s API documentation. For Oracle JET API documentation, see API Reference for Oracle® JavaScript Extension Toolkit (Oracle JET). Select the component you're interested in viewing from the API list.

About Oracle JET Component Validation Methods

Oracle JET editable elements support the following methods for validation purposes. For details on how to call this method, its parameters and return values, refer to the component’s API documentation.

Element Method Description

reset()

Use this method to reset the element by clearing all messages and messages attributes - messagesCustom - and update the element’s display value using the attribute value. User entered values will be erased when this method is called.

validate()

Use this method to validate the component using the current display value.

For details on calling an element's method, parameters, and return values, see the Methods section of the element's API documentation in API Reference for Oracle® JavaScript Extension Toolkit (Oracle JET). You can also find detail on how to register a callback for or bind to the event and for information about what triggers the events. Select the component you're interested in viewing from the API list.

About Oracle JET Converters

Oracle JET provides converters to convert date, date-time, number, color, and string.

These converters extend the Converter object which defines a basic contract for converter implementations. The converter API is based on the ECMAScript Internationalization API specification (ECMA-402 Edition 1.0) and uses the Unicode Common Locale Data Repository (CLDR) for its locale data. Both converters are initialized through their constructors, which accept options defined by the API specification. For additional information about the ECMA-402 API specification, see https://www.ecma-international.org/publications-and-standards/standards/ecma-402/. For information about the Unicode CLDR, see http://cldr.unicode.org.

The Oracle JET implementation extends the ECMA-402 specification by introducing additional options. You can use the converters with an Oracle JET component or instantiate and use them directly on the page. Each converter has a ConverterOptions type definition that specifies the conversion options it supports. The following table describes the available converters and provides a link to the API documentation for each converter, including detailed descriptions of the properties supported by each converter’s ConverterOptions type definition.

Converter Description Link to API

ColorConverter

Converts Color object formats

ColorConverter

IntlDateTimeConverter

Parses a string into an ISO string format (yyyy-mm-dd) or converts an ISO string into a standard string format. For example:

  • 10/12/2020 ---> 2020-10-12
  • 2020-10-12 ---> 10/12/2020

IntlDateTimeConverter

NumberConverter

Converts a string into a number and formats a number into a locale-specific string

NumberConverter

BigDecimalStringConverter

Converts a big-decimal string into a locale-specific string. Use for very large numbers (greater than Number.MAX_SAFE_INTEGER) and numbers with large scale (more than 17 fractional digits). Can only be used with components that expect a string value, such as oj-c-input-text.

BigDecimalStringConverter

LocalDateConverter

Converts a date-only ISO string to a formatted string or a string to a date-only ISO string

LocalDateConverter

The Oracle JET converters support lenient number and date parsing when the user input does not exactly match the expected pattern. The parser does the lenient parsing based on the leniency rules for the specific converter. For example, both NumberConverter and BigDecimalStringConverter remove unexpected characters. The ConverterOptions type definition typically includes a lenientParse property that you can set to none so that user input matches the expected input or an exception is thrown. For specific details, see the API documentation for the converter that you are using.

The resource bundles that hold the locale symbols and data used by the Oracle JET converters are downloaded automatically based on the locale set on the page when using RequireJS and the ojs/ojvalidation-datetime or ojs/ojvalidation-number module. If your app does not use RequireJS, the locale data will not be downloaded automatically.

You can use the converters with an Oracle JET component or instantiate and use them directly on the page.

Use Oracle JET Converters with Oracle JET Components

Oracle JET components that accept user input, such as oj-c-input-date-text, already include an implicit converter that parses user input. However, you can also specify an explicit converter on the component that will be used instead when converting data.

In the following example, the oj-c-input-date-text component displays an error message if you enter "abc" while it renders input of "12/25/24" as "12/25/2024" using its implicit converter.

The error that the converter throws when there are errors during parsing or formatting operations is represented by the ConverterError object, and the error message is represented by an object of type Message. The messages that Oracle JET converters use are resources that are defined in the translation bundle included with Oracle JET.


oj-c-input-date-text with implicit converter

You can also specify the converter directly on the component's converter attribute, if it exists. For example, the oj-c-input-date-text component uses the converter attribute to change the date style that the component renders to one of the converter options supported by LocalDateConverter.


oj-c-input-date-text with explicit converter

Understand Time Zone Support in Oracle JET

Oracle JET input components, such as oj-input-date-time, support local time zone input. You can enable time zone support by using Oracle JET's IntlDateTimeConverter, which relies on JavaScript's Intl.DateTimeFormat API.

In the following image, the Input Date Time component’s converter attribute references code that renders the input time of 2013-12-02T04:00:00Z appropriately depending on whether the time zone is America/Los_Angeles or Asia/Hong_Kong.

For more information about Oracle JET's IntlDateTimeConverter, see IntlDateTimeConverter.

About Oracle JET Validators

Oracle JET validators provide properties that allow callers to customize the validator instance. The properties are documented as part of the validators’ API. Unlike converters where only one instance of a converter can be set on an element, you can associate one or more validators with an element.

When a user interacts with the element to change its value, the validators associated with the element run in order. When the value violates a validation rule, the value attribute is not populated, and the validator highlights the element with an error.

You can use the validators with an Oracle JET element or instantiate and use them directly on the page.

Use Oracle JET Validators with Oracle JET Components

Oracle JET editable elements, such as oj-input-text and oj-input-date, set up validators both implicitly, based on certain attributes they support such as required, min, max, and so on, and explicitly by providing a means to set up one or more validators using the component's validators attribute.

For example, the following code shows an oj-input-date element that uses the default validator supplied by the component implicitly. When the oj-input-date component reads the min and max attributes, it creates the implicit DateTimeRangeValidator.

import { h } from "preact";
import * as ConverterUtilsI18n from "ojs/ojconverterutils-i18n";
import "ojs/ojformlayout";
import "ojs/ojdatetimepicker";

export function Content() {
  let todayIsoDate: string = ConverterUtilsI18n.IntlConverterUtils.dateToLocalIso(
    new Date()
  );

  let milleniumStartIsoDate: string = ConverterUtilsI18n.IntlConverterUtils.dateToLocalIso(
    new Date(2000, 0, 1)
  );

  return (
    <div class="oj-web-applayout-max-width oj-web-applayout-content">
      <oj-form-layout id="formLayout1" columns={1}>
        <oj-input-date
          id="dateTimeRange1"
          min={milleniumStartIsoDate}
          max={todayIsoDate}
          labelHint="'min' attribute and 'max' option"
        ></oj-input-date>
      </oj-form-layout>
    </div>
  );
}

When the user runs the page, the oj-input-date element displays an input field with a calendar icon. If you input data that is not within the expected range, the built-in validator displays an error message with the expected range.

Description of input-validator-message-vdom.png follows
Description of the illustration input-validator-message-vdom.png

The error thrown by the Oracle JET validator when validation fails is represented by the ValidatorError object, and the error message is represented by an object of type Message. The messages and hints that Oracle JET validators use when they throw an error are resources that are defined in the translation bundle included with Oracle JET.

You can also specify the validator on the element's validators attribute, if it exists. The code sample below shows another oj-input-date element that calls a function which specifies the DateTimeRangeValidator validator (dateTimeRange) in the validators attribute.

import { h } from "preact";
import { useMemo } from "preact/hooks";
import * as ConverterUtilsI18n from "ojs/ojconverterutils-i18n";
import "ojs/ojformlayout";
import "ojs/ojdatetimepicker";
import AsyncDateTimeRangeValidator = require("ojs/ojasyncvalidator-datetimerange");
import * as DateTimeConverter from "ojs/ojconverter-datetime";

export function Content() {
  let todayIsoDate: string = ConverterUtilsI18n.IntlConverterUtils.dateToLocalIso(
    new Date()
  );

  let milleniumStartIsoDate: string = ConverterUtilsI18n.IntlConverterUtils.dateToLocalIso(
    new Date(2000, 0, 1)
  );

  const dateRange = useMemo(() => {
    return [
      new AsyncDateTimeRangeValidator({
        max: todayIsoDate,
        min: milleniumStartIsoDate,
        hint: {
          inRange:
            "Enter a date that falls in the current millennium and "
            + "is not greater than today's date.",
        },
        converter: new DateTimeConverter.IntlDateTimeConverter({
          day: "2-digit",
          month: "2-digit",
          year: "2-digit",
        }),
      }),
    ];
  }, [todayIsoDate, milleniumStartIsoDate]);

  return (
    <div class="oj-web-applayout-max-width oj-web-applayout-content">
      <oj-form-layout id="formLayout1" columns={1}>
        <oj-input-date
          id="dateTimeRange2"
          labelHint="'dateTimeRange' type in 'validators' option"
          validators={dateRange}
        ></oj-input-date>
      </oj-form-layout>
    </div>
  );
}

When the user runs the page for the en-US locale, the oj-input-date element displays an input field that expects the user's input date to be between 01/01/2000 and the current date. When entering a date value into the field, the date converter accepts alternate input as long as it can parse it unambiguously. This offers end users a great deal of leniency when entering date values. For example, typing 1-2-3 converts to a Date that falls on the 2nd day of January, 2003. If the Date value also happens to fall in the expected Date range set in the validator, then the value is accepted. If validation fails, the component displays an error.

Oracle JET elements can also use a regExp validator. If the regular expression pattern requires a backslash, while specifying the expression within an Oracle JET element, you need to use double backslashes. The options that each validator accepts are specified in API Reference for Oracle® JavaScript Extension Toolkit (Oracle JET).

Use Custom Validators in Oracle JET

You can create custom validators in Oracle JET that you can reference like built-in validators from the validators attribute.

The following image shows a custom validator that displays an error message if the user’s password doesn’t match.

To create and use a custom validator:

  1. Reference the custom validator from the validators attribute of your Oracle JET component.
    In the following example, the component that renders the Confirm Password input field references a confirmPasswordValidator variable from its validators attribute.
    <oj-c-input-password
       id="cpassword"
       value={passwordRepeat}
       onvalueChanged={handlePasswordRepeat}
       validators={confirmPasswordValidator}
       label-hint="Confirm Password"
       mask-icon="visible">
      </oj-c-input-password>
  2. Write your custom validator.
    In the following example, the validateEqualToPassword function validates that the value entered in the Confirm Password field matches the value previously entered in the Password field. The confirmPasswordValidator variable uses a useMemo hook to ensure that confirmPasswordValidator only recomputes when validateEqualToPassword changes.
    . . . 
    const validateEqualToPassword = useCallback(
      (value: string) => {
        if (value && value !== password) {
          throw new Error(
            "The passwords must match!"
          );
        }
        setError(null);
        return Promise.resolve();
      },
      [password]
    );
    
    const confirmPasswordValidator = useMemo(
      () => [{ validate: validateEqualToPassword }],
      [validateEqualToPassword]
    );
    . . .

About Asynchronous Validators

Oracle JET input components support asynchronous server-side validation via the validators attribute. That means you can check input values against server data without the need to submit a form or refresh a page.

Two example scenarios illustrate where you can use asynchronous server-side validation:

  • In a form that collects new user data, you validate input in an email field to check if the input value has been registered previously.
  • Set number range validators that check against volatile data. For example, on an e-commerce website, you can check the user’s cart against the available inventory and inform the user if the goods are unavailable without them submitting the cart for checkout.

The following code shows an oj-c-input-text element with the validators attribute set to an array of functions (validators and asyncValidator). The synchronous validator, validators, checks if the input value is 500. If it is, an error is thrown. The asynchronous validator, asyncValidator, returns a Promise. A Promise represents a value that may not be available yet but will be resolved at some point in the future. The Promise evaluates to true if validation passes and if validation fails, it returns an error. AsyncNumberRangeValidator checks if the input value falls within a specified range (100 to 1000). It simulates a server-side delay using setTimeout before performing the validation. The validators attribute must be of type AsyncValidator to fulfill the API contract required to create the asynchronous validator.

import { h } from "preact";
import "ojs/ojformlayout";
import { useState } from "preact/hooks";
import AsyncNumberRangeValidator = require("ojs/ojasyncvalidator-numberrange");
import { CInputTextElement } from "oj-c/input-text";
import "oj-c/input-text";

export function Content() {
  const [quantityLimit, setQuantityLimit] = useState("");

  const onValueChanged = (event: CInputTextElement.valueChanged<string>) => {
    setQuantityLimit(event.detail.value);
  };

  const minQuantity = 100;
  const maxQuantity = 1000;

  // synchronous validator.
  const validators = {
    validate: (value: string | number) => {
      if (value === 500 || value === "500") {
        throw new Error("500 is invalid");
      } else {
        return;
      }
    },
    getHint: () => {
      return "To see an immediate error from the synchronous validator (validators), enter 500.";
    },
  };

  // asynchronous validator.
  const asyncValidator = {
    validate: (value: string | number) => {
      const numberRangeValidator = new AsyncNumberRangeValidator({
        min: minQuantity,
        max: maxQuantity,
      });

      return new Promise<void>((resolve, reject) => {
        // Simulate server-side delay
        setTimeout(() => {
          numberRangeValidator.validate(value).then(
            () => {
              resolve();
            },
            (e) => {
              // Handle validation error
              reject(
                new Error(
                  `${value} is not in the accepted range of ${minQuantity} - ${maxQuantity}`
                )
              );
            }
          );
        }, 1000);
      });
    },

    hint: new Promise<string>((resolve) => {
      // Simulate server-side delay 
      setTimeout(() => {
        resolve(
          `To see an error from the asynchronous validator (asyncValidator) that appears after 1 second, 
          enter a number outside the range of ${minQuantity} - ${maxQuantity}`
        );
      }, 100);
    }),
  };

  return (
    <div class="oj-web-applayout-max-width oj-web-applayout-content">
      <oj-form-layout columns={1} class="oj-md-margin-4x-horizontal">
        <oj-c-input-text
          id="input-text"
          labelHint="Quantity Limit"
          onvalueChanged={onValueChanged}
          validators={[validators, asyncValidator]}
          value={quantityLimit}
        ></oj-c-input-text>
      </oj-form-layout>
    </div>
  );
}

For more information, see the validators attribute section of Input Text or see Promise (MDN).