Form

Example

<vega-form data-vega-form="form">
        <vega-input data-vega-form="input" min="6" value="3" label="Min value is 6"></vega-input><br>
        <vega-form data-vega-form="nestedForm">
            <vega-input data-vega-form="nestedInput" max="12" value="15" Label="This is a nested form input"></vega-input>
        </vega-form>

        <vega-button id="getValue">Get Value</vega-button>
        <vega-button id="valid">Validate</vega-button>
        <vega-button id="reset">Reset</vega-button>
</vega-form>

const vegaForm =  document.querySelector('vega-form');
document.getElementById('getValue').addEventListener('vegaClick', async () => {
			window.alert(JSON.stringify(await vegaForm.getValue(), null, 2));
		});

document.getElementById('reset').addEventListener('vegaClick', () => {
    vegaForm.reset();
});     

document.getElementById('valid').addEventListener('vegaClick', () => {
			vegaForm.valid();
		});

Usage

The vega-form component is form logic helper designed to control vega field components (e.g. vega-input, vega-select, vega-input, etc.) in an aggregate way. The advantage is that instead of needing to loop through each of the elements to access the form data, with vega-form you have access to all of the data from a single touchpoint.

The features include the following:

  • You can use different methods exposed from vega-form to do form reset/validation/prefill.
  • You can also use the isValid property to track the form validation status in a reactive way.
  • Nested inputs enable you to create nested data as subcategories.

Properties

There are no specific properties which can be used for controlling the form component’s UI/UX, however there are a couple of components which can be used by developers who work with various states within vega-form.

Using getValue

You can retrieve the value for all available field elements in the vega-form asynchronously.

Use the following structure:

getValue<T>(option?: FormControlOption) => Promise<T>

This will return

Type: Promise<T>

For example:

const vegaForm = document.querySelector('vega-form');
const formValue = await vegaForm.getValue();
console.log(formValue);

isValid

isValid is a Boolean which will indicate whether specific elements in a form are valid when given a set of validation criteria. A field will be controllable by the form if the field is one of the following:

  • vega-input
  • vega-select-input
  • vega-radio-group
  • vega-checkbox-group
  • vega-date-picker
  • vega-stepper

and that the element is not disabled and invisible.

isTouched

isTouched is a Boolean object, which indicates if values of specific controlled elements within the form are changed. It is set to true if values have been changed.

If any elements have been changed, isTouched will be set to true. When used for resetting a form; when the form is reset it will be set to false. It is also set to false when called by the setValue method.

data-vega-form

This is not technically a property of vega-form but is instead a necessary flag used for form elements so that they may be controlled by either of the above elements.

For more details, view in Storybook

Events

vegaValidate

vegaValidate is dipatched when the validation result of the form is changed.

vegaFormSubmit

If the form is valid, the vegaFormSubmit event is triggered when selecting submit or using the Enter key. The form details is dispatched with this event.

vegaFormReset

Dispatched when the Reset button is selected, the form will reset the value and then dispatch the event.

React

<VegaForm
    onVegaFormValidate={ (e) => { console.log(e.detail) } }
    onVegaFormSubmit={ (e) => { console.log(e.detail) } }
    onVegaFormReset={ (e) => { console.log(e.detail) } }
    ...
></VegaForm>

Vue.js

<vega-form @vegaformvalidate="method" @vegaformsubmit="method" @vegaformreset="method" ...></vega-form>

JavaScript

document.getElementById("vega-form").addEventListener("vegaValidate", (e) => {
    console.log(e.detail)
})
document.getElementById("vega-form").addEventListener("vegaFormSubmit", (e) => {
    console.log(e.detail)
})
document.getElementById("vega-form").addEventListener("vegaFormReset", (e) => {
    console.log(e.detail)
})

Methods

vega-form proactively controls form status by specifying a manual trigger for the control flow.

Note: we currently support vega-input, vega-select-input, vega-radio-group, vega-checkbox-group, vega-date-picker, vega-stepper, and vega-form as field components.

You may use the following optional method for FormControlOption parameters.

type FormControlOption = {
    skipDisabled?: boolean;
    skipInvisible?: boolean;
};

The following valid method retrieves validation results for all vega field elements nested in the vega-form asynchronously. If the element is a vega-form, it will call the valid() method recursively.

valid(showError?: boolean | 'rule') => Promise<{ isValid: boolean; invalidFields: string[]; }>

The value options for showError are true, false and ‘rule’. If set to true, it will override the shouldShowError method for all rules and will always show errors if they exist. True is also the default value. If set to false it will override the shouldShowError method for all rules and won’t show any errors. If set to ‘rule’, it will allow the shouldShowError logic to decide when to show errors.

The above method returns:

Type: Promise<{ isValid: boolean; invalidFields: string[]; }>.

Example

If we have a vega-form with the below structure:

<vega-form data-vega-form="form">
    <vega-input data-vega-form="input" min="6" value="3"></vega-input>
    <vega-form data-vega-form="nestedForm">
        <vega-input data-vega-form="nestedInput" max="12" value="15"></vega-input>
    </vega-form>
</vega-form>

You can perform a validation using the following:

const vegaForm = document.querySelector('vega-form');
const validationResult = await vegaForm.valid();
console.log(validationResult);

This will then return:

{
    "isValid": false,
    "invalidFields": ["input", "nestedInput"]
}

If you do not wish to display a validation error, pass false to VegaForm#valid()

const validationResult = await vegaForm.valid(false);

getValue

getValue uses the following structure:

getValue<T>(option?: FormControlOption) => Promise<T>

which will then return

Promise<T>

Example

If we have a vega-form with the following structure:

<vega-form data-vega-form="form">
    <vega-input data-vega-form="input" value="inputValue"></vega-input>
    <vega-form data-vega-form="nestedForm">
        <vega-input data-vega-form="nestedInput" value="nestedInputValue"></vega-input>
    </vega-form>
</vega-form>

You can retrieve the value using:

const vegaForm = document.querySelector('vega-form');
const formValue = await vegaForm.getValue();
console.log(formValue);

This returns:

{
    "input": "inputValue",
    "nestedForm": { "nestedInput": "nestedInputValue" }
}

setValue

setValue uses the following structure:

setValue<T>(value: T, option?: FormControlOption) => Promise<void>

which will then return

Promise<void>

Example

If we have a vega-form with the following structure:

<vega-form data-vega-form="form">
    <vega-input data-vega-form="input"></vega-input>
    <vega-form data-vega-form="nestedForm">
        <vega-input data-vega-form="nestedInput"></vega-input>
    </vega-form>
</vega-form>

The form value can be set by running:

const vegaForm = document.querySelector('vega-form');
await vegaForm.setValue({
    input: 'inputValue',
    nestedForm: { nestedInput: 'nestedInputValue' },
});
console.log(await vegaForm.getValue());

which will then return

{
    "input": "inputValue",
    "nestedForm": { "nestedInput": "nestedInputValue" }
}

reset

To reset the form, use the following structure:

reset(defaultValueMap?: unknown, option?: FormControlOption) => Promise<void>

Example:

async reset(defaultValueMap: unknown = {}, option?: { skipDisabled?: boolean; skipInvisible?: boolean; }): Promise<void>

This returns

Type: Promise<void>

Example

If we have a vega-form with the following structure:

<vega-form data-vega-form="form">
    <vega-input data-vega-form="input" value="inputValue"></vega-input>
    <vega-form data-vega-form="nestedForm">
        <vega-input data-vega-form="nestedInput" value="nestedInputValue"></vega-input>
    </vega-form>
</vega-form>

You an reset the form value by running:

const vegaForm = document.querySelector('vega-form');
await vegaForm.reset();
console.log(await vegaForm.getValue());

This returns

{
    "input": null,
    "nestedForm": { "nestedInput": null }
}

Note

If you want to reset the form with a pre-defined default value for specific fields, you can add the value object with the same structure as the output of VegaForm#getValue():

const vegaForm = document.querySelector('vega-form');
await vegaForm.reset({ input: '', nestedForm: { nestedInput: '' } });
console.log(await vegaForm.getValue());

This returns:

{
    "input": "",
    "nestedForm": { "nestedInput": "" }
}

Validation Rules

For each form component, the validation-rules field will consume the ValidationRule array. This array can only be attached to the input.

Use the formFieldValidationRule interface.

export type EvaluateResult = {
    isValid: boolean;
    message: string;
};

export type FormFieldStatusMeta = {
    touched: boolean;
    modified: boolean;
    disabled: boolean;
    visible: boolean;
};
export interface FormFieldValidationRule<T> extends ValidationRule<T> {
    canEvaluate(
        input: unknown,
        status?: FormFieldStatusMeta,
        statusChanged?: (keyof FormFieldStatusMeta)[],
    ): boolean;
    shouldShowError?(status?: FormFieldStatusMeta, statusChanged?: (keyof FormFieldStatusMeta)[]): boolean;
    evaluate(input: T): EvaluateResult;
}

**Note:""

  1. The statusChanged array allows four strings: touched, modified, disabled, visible.
  2. The shouldShowError function is optional and can be used for indicating whether or not to show an error message. If not provided, the default rule is !status.disabled && status.visible && (status.touched || status.modified)

Example

const StringContainsVegaRule = {
    canEvaluate(input: unknown, status: FormFieldStatusMeta): boolean {
        return typeof input === 'string' && !status.disabled && status.visible;
    }

    shouldShowError(
        status: FormFieldStatusMeta,
        statusChanged: (keyof FormFieldStatusMeta)[],
    ): boolean {
        return (
            !status.disabled &&
            status.visible &&
            statusChanged.includes('modified') &&
            (status.touched || status.modified)
        );
    }

    evaluate: (input: string) => {
        const isValid = input.indexOf('vega') > -1;
        return { isValid, message: isValid ? '' : 'Input must contain "vega"' };
    },
};

<VegaInput validation-rules = {[StringContainsVegaRule]} .. ></VegaInput>

Methods

In vega-form, the form status is controlled proactively by specifying a manual trigger for the control flow.

The following field components are supported within vega-form:

  • vega-input
  • vega-select-input
  • vega-radio-group
  • vega-checkbox-group
  • vega-date-picker
  • vega-stepper
  • vega-form

For vega-form to be able to recognize the above components, the property data-vega-form="<field_name>" must be set for each component. The <field-name> can be used as an identifier during form control for each field.

All exposed methods in vega-form are asynchronous.

FormControlOption parameters

type FormControlOption = {
    skipDisabled?: boolean;
    skipInvisible?: boolean;
};

This method:

valid(showError?: boolean | 'rule') => Promise<{ isValid: boolean; invalidFields: string[]; }>

returns:

Promise<{ isValid: boolean; invalidFields: string[]; }>

Example:

If we have a vega-form with the following structure:

<vega-form data-vega-form="form">
    <vega-input data-vega-form="input" min="6" value="3"></vega-input>
    <vega-form data-vega-form="nestedForm">
        <vega-input data-vega-form="nestedInput" max="12" value="15"></vega-input>
    </vega-form>
</vega-form>

We can validate it with:

const vegaForm = document.querySelector('vega-form');
const validationResult = await vegaForm.valid();
console.log(validationResult);

returning:

{
    "isValid": false,
    "invalidFields": ["input", "nestedInput"]
}

If you do not wish to show the validation error message, set vegaFormValid() to be false:

const validationResult = await vegaForm.valid(false);

Accessibility

  • Elements must only use allowed ARIA attributes
  • Required ARIA attributes must be provided
  • ARIA roles used must conform to valid values
  • ARIA attributes must conform to valid values
  • ARIA attributes must conform to valid names
  • autocomplete attribute must be used correctly
  • Buttons must have discernible text
  • Elements must have sufficient color contrast
  • id attribute value must be unique
  • Form field must not have multiple label elements
  • Form elements must have labels
  • Interactive controls must not be nested

Best Practices

Keep forms short. Forms should generally include only the most necessary information you need from the user.

Use a UX-guided order. When ordering a form, structure it from a user’s perspective, not from the perspective of a database or application. Since data can typically be entered in any order, it’s best to put together the information in a logical order that will make sense to your target audience.

Grouping. Group information about things that are related to each other. Use of the nested inputs should help make this easier to include sub-arrays of data together, but make sure that information is presented in a way that makes the most sense to a user.

Single columns. Forms should typically be single-column in structure. Multiple columns can create an ambiguous workflow to the user, particularly if fields are aligned directly next to each other,

Match the input to the content. Use appropriate fields - make sure that type of input matches the type of content being requested. For example, don’t use a single-line input when you are requesting a larger amount of text, which would be better represented with a textarea field.