be-reformable is a custom element enhancement that (progressively) enhances the built-in form element, making attributes/properties like action dynamic. It does not do anything fetch related, leaving that for other components / enhancements. It provides the mechanics of specifying what the fetch parameters should be, and quietly suggests the appropriate time to perform said fetch.
It uses assign-gingerly as the underpinning approach, as opposed to the controversial "is" extension.
Let's see how we can use be-reformable to bind input elements to the action property. These input elements should not have a name attribute, as we don't want them to affect the query string. Instead we use a custom attribute starting with ":".
In this example, we bind to the newton advanced math micro service, declaratively. By itself, this enhancement will not make the form fully functional for this service (as it doesn't do a fetch or anything).
<link id=newton-microservice rel=preconnect href=https://newton.now.sh/ >
<form
be-reformable='{
"baseLink": "newton-microservice",
"path": "api/v2/:operation/:expression",
}'
>
<label>
Operation:
<input :operation value=integrate>
</label>
<label>
Expression:
<input :expression value="x^2">
</label>
<noscript>
<button type=submit>Submit</button>
</noscript>
</form>The "path" value follows the URL Pattern syntax.
"base-link" is optional, but allows for easy management of common base API URL's across the application. The link tag should probably go in the head tag of index.html (typically).
What be-reformable does is:
- By default, adds "input" event to the adorned form element.
- If the form's checkValidity() is false, ignores the event.
- When the event occurs, and checkValidity() is true, it uses the baseLink + path to set the action value of the form element.
- It pulls in all the form associated custom elements and/or built-in input elements referenced by the path property.
- Forms the compound string and sets the action property/attribute.
- Triggers event "fetch-ready" which provides the recommended url and options parameters. Event is dispatched both from the form element as well as the enhancement.
Note
A VSCode plug-in is available to make editing json-in-html more pleasant. This extension works with the web interface of vscode.
be-reformable is a rather lengthy name, and if it appears frequently within an application, could get tiresome to have to type. It is the canonical name, but developers can easily define alternative names. This package provides one such alternative:
<form
πΊ='{
"baseLink": "newton-microservice",
"path": "api/v2/:operation/:expression",
}'
>...</form>This is also supported:
<form
πΊ-base-link=newton-microservice πΊ-path=api/v2/:operation/:expression
>...</form>be-reformable supports protocol-prefixed values for resolving configuration from external sources. This is powered by assign-gingerly's protocol resolution and "..." spread key support.
Values in the attribute JSON can use protocol prefixes to reference external data:
protocolName://key?.optionalPath
protocolNameβ the registered protocol handler (e.g.,globalThis,localStorage,sessionStorage)keyβ passed to the protocol handler to retrieve the source object?.optionalPathβ optional path resolved against the retrieved object using?.-delimited navigation
When a key is "...", its resolved value is spread (merged) into the parent object. This works at any nesting level.
<link id=newton-microservice rel=preconnect href=https://newton.now.sh/ >
<script>
globalThis['rPpwNLcYsUOjFcg+N8lmOA'] = {
myCustomHeader: 'goodbye'
}
</script>
<form be-reformable='{
"baseURL": "globalThis://newton-microservice?.href",
"path": "api/v2/:operation/:expression",
"headerFields": ["#myHeader"],
"headers": {
"...": "globalThis://rPpwNLcYsUOjFcg+N8lmOA"
}
}'>
<label>
header:
<input id=myHeader value=hello>
</label>
<label>
Operation:
<input :operation value=integrate>
</label>
<label>
Expression:
<input :expression value="x^2">
</label>
</form>In this example:
"baseURL": "globalThis://newton-microservice?.href"resolves by:- Looking up
globalThis['newton-microservice']β the<link>element (elements with anidare accessible onglobalThis) - Navigating
?.hrefon the result β yielding"https://newton.now.sh/"
- Looking up
"headers": { "...": "globalThis://rPpwNLcYsUOjFcg+N8lmOA" }resolves by:- Looking up
globalThis['rPpwNLcYsUOjFcg+N8lmOA']β the object{ myCustomHeader: 'goodbye' } - Spreading that object into
headers, soheadersbecomes{ myCustomHeader: 'goodbye' }
- Looking up
The globalThis protocol is registered by default in be-reformable. It resolves keys via globalThis[key], which covers:
- Elements with an
idattribute (accessible asglobalThis['elementId']) - Any value explicitly set on
globalThis(e.g.,globalThis['myKey'] = { ... })
The headerFields property accepts CSS selectors for input elements whose values become headers:
"#myHeader"β selects by id, uses the element'svalueas the header value, with the id as the header name
Note
Other components / enhancements that leverage this enhancement, and actually perform the fetch should consider use of be-hashing-out or some other security mechanism if there's any sense of danger that justifies adding that security check.
<link id=newton-microservice rel=preconnect href=https://newton.now.sh/ >
<form
be-reformable='{
"baseLink": "newton-microservice",
"path": "api/v2/:operation/:expression",
"submitOptions":{
"onlyAfter": "@submit::click",
"nudges": true,
"disableIfNotAllConditionsAreMet": true
}
}'
>
<label for=operation>
Operation:
<input :operation value=integrate>
</label>
<label for=expression>
Expression:
<input :expression value="x^2">
</label>
<noscript>
<button type=submit>Submit</button>
</noscript>
<button disabled type=button name=submit>Submit</button>
</form>Any web server that serves static files with server-side includes will do but...
- Install git
- Fork/clone this repo
- Install node.js
- Open command window to folder where you cloned this repo
-
git submodule add https://github.com/bahrus/types.git types
-
git submodule update --init --recursive
-
npm install
-
npm run serve
- Open http://localhost:8000/demo/ in a modern browser
import 'be-reformable/be-reformable.js';<script type=module crossorigin=anonymous>
import 'https://esm.run/be-reformable';
</script>