Skip to content

Add additional validations on the form element, beyond those that can be specified on an individual field level.

License

Notifications You must be signed in to change notification settings

bahrus/be-formidable

Repository files navigation

be-formidable (🥷)

Playwright Tests How big is this package in your project? NPM version

None Shall Pass

Add additional validations on the form element, beyond those that can be specified on an individual field level.

<form be-formidable='{
    "invalidIf":[
        {
            "noneOf": ["@url", "@file"],
            "invalidCssClass": "no-url-or-file-selected"
        }
    ]
}'>
    <label>
        URL:
        <input name=url id=url type=url>
    </label>
    <label>
        File:
        <input name=file id=file type=file>
    </label>
</form>

Attaching this enhancement / behavior results in overriding the checkValidity() method of the form element. Calls to checkValidity has the added side-effect of modifying css classes on the form element. If the validations fail, css class "invalid" is added to the form element. Otherwise "valid" is added. In addition, if the "invalidCssClass" property is specified, it will get applied to the form element, or removed, depending on the validation.

Shorter, alternative names

It is easy to define a custom, (shorter) name for less formal environments. be-formidable is the canonical name, but by referencing an alternative registration file we can use, for example:

<form 🥷🏾='{
    "invalidIf":[
        {
            "noneOf": ["@url", "@file"],
            "invalidCssClass": "no-url-or-file-selected"
        }
    ]
}'>
    <div class=instructions>Please Select a url or a file</div>
    <label>
        URL:
        <input name=url id=url type=url>
    </label>
    <label>
        File:
        <input name=file id=file type=file>
    </label>
</form>

Why two negatives?

It could be argued that validOnlyIf/oneOf is clearer than invalidIf/noneOf. Here's why we chose the more counterintuitive approach:

This component prefers the "innocent until proven guilty" way of thinking, because it feels a bit truer to what it is actually doing, and may more effectively alert the developer to the fact that the form will be considered valid until the be-formidable behavior / enhancement is attached. Before then, it might be prudent to hide/disable any submit buttons, or even hide or obscure the entire form.

JSON-in-html?

Editing JSON-in-html can be rather error prone. A VS Code extension is available to help with that, and is compatible with web versions of VSCode.

And in practice, it is also quite ergonomic to edit these types of attributes in a *.mjs file that executes in (no)de(no) as the file changes, and compiles to an html file via the be-importing compiler, for example. This allows the attributes to be editable with JS-like syntax. Typescript 4.6 supports compiling mts to mjs files, which then allows typing of the attributes. Examples of this in practice are:

  1. xtal-side-nav
  2. xtal-editor
  3. cotus

Specifying property to check for truthiness [TODO]

The specifier for which elements to monitor follows the DSS syntax.

By default, the "value" property is what is used on the element when it is checked for truthiness.

To specify an alternative property to check:

<form be-formidable='{
    "invalidIf":[
        {
            "noneOf": ["@keysInPocket.checked", "@havePhone.checked"],
            "invalidCssClass": "not-ready-to-go-out"
        }
    ]
}'>
    <div class=instructions>
        Please take your keys, or at least a phone to call a locksmith.
    </div>
    <label>
        <input name=keysInPocket type=checkbox>
        I have keys in my pocket
    </label>
    <label>
        <input name=havePhone type=checkbox>
        I have a phone
    </label>
</form>

So this syntax is not compatible with form elements that use "." in the name. If encountering a scenario where "." may be in the name, we need to be a bit more verbose:

<form be-formidable='{
    "invalidIf":[
        {
            "noneOf": [
                {
                    "name": "keys.in.pocket",
                    "prop": "checked"
                 },
                 {
                     "name": "have.phone",
                     "prop": "checked"
                 }
            ],
            "instructions": "Please take your keys, or at least a phone to call for a locksmith.",
            "invalidCssClass": "Not ready to go out."
        }
    ]
}'>
   ...
</form>

Other validation criteria [Untested]

The rules so far have essentially been providing support for the "required" attribute, but for groups of form elements. But there are other validations that form fields support (min, max, pattern, etc).

Support for such criteria is provided. For example with min:

<form be-formidable='{
    "invalidIf":[
        {
            "noneOf": [
                {
                    "name": "firstCustomer Age",
                    "min": 17,
                },
                {
                    "name": "secondCustomer Age",
                    "min": 17,
                },
                {
                    "name": "thirdCustomer Age",
                    "min": 17,
                },
            ],
            "instructions": "No one under the age of 17 is permitted to watch this movie without being accompanied by an adult or guardian"
        }
    ]
}'>
   ...
</form>

NotEquals

<form be-formidable='{
    "validOnlyIf":[
        {
            "atLeastOneOf": [
                {
                    "name": "firstCustomer Age",
                    "min": 17,
                },
                {
                    "name": "secondCustomer Age",
                    "min": 17,
                },
                {
                    "name": "thirdCustomer Age",
                    "min": 17,
                },
            ],
            "equals": [
                {
                    "name": "hiddenCalculatedField"
                },
                {
                    "name": "payment"
                }
            ]
            "instructions": "No one under the age of 17 is permitted to watch this movie without being accompanied by an adult or guardian"
        }
    ]
}'>
   ...
</form>

Specify querySelectorAll() for the elements to be checked

<form be-formidable='{
    "invalidIf":[
        {
            "noneOf": [{
                "find": ".my-form-element-group", 
                "prop": "checked"
            }],
        }
    ]
}'>
    ...
</form>

Side effect of calling checkValidity()

"invalidClassesToApply" is an array of strings that is stored at location formElement.beEnhanced.beFormidable.invalidClassesToApply. It lists validation errors.

Specify to monitor for certain events.

As mentioned in the beginning, the examples so far do not result in automatically calling checkValidity (except, by default as soon as the behavior is attached), so nothing will actually happen unless some other script is invoking checkValidity.

We can specify when to automatically call checkValidity.

Simplest:

<form be-formidable='{
    "invalidIf":[
        {
            "noneOf": ["url", "file"],
            "message": "Select a url or a file"
        }
    ],
    "checkValidityOn": "input"
}'>
    ...
</form>

If multiple events / optional settings are required:

<form be-formidable='{
    "invalidIf":[
        {
            "noneOf": ["url", "file"],
            "message": "Select a url or a file"
        }
    ],
    "checkValidityOn": [
        "input", 
        {
            "type": "change",
            "options": {
                "capture": true
            }
        }
    ]
}'>
    ...
</form>

Unfortunate headwinds

Unfortunately, the platform does not yet support specifying a custom validation function for a form element.

This means that the :valid and :invalid pseudo-classes are not adjusted as needed.

The be-formidable enhancement does set "valid" or "invalid" classes during the checkValidity() calls.

Suggested CSS:

<style>
    form:invalid, form.be-formidable.invalid {
        border: 5px solid red;
    }
    form:valid, form.be-formidable.valid {
        border: 5px solid green;
    }
</style>

Viewing Locally

Any web server that serves static files will do but...

  1. Install git.
  2. Fork/clone this repo.
  3. Install node.
  4. Open command window to folder where you cloned this repo.
  5. npm install

  6. npm run serve

  7. Open http://localhost:8000/demo in a modern browser.

Importing in ES Modules:

import 'be-formidable/be-formidable.js';

Using from CDN:

<script type=module crossorigin=anonymous>
    import 'https://esm.run/be-formidable';
</script>

About

Add additional validations on the form element, beyond those that can be specified on an individual field level.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published