Core Component Types
Core component types are the backbone of SmarkForm:
📖 Table of Contents
Complex field types
type: form and type: list
📋 form and 📋 list provide the structure that allows to handle any possible JSON data including nested objects and arrays.
-
form imports and exports plain JSON objects.
-
list imports and exports JSON arrays.
Scalar field types
Notice that the SmarkForm field types are not necessarily related to the type attribute of
<input>
tags even they can limit it’s allowed values.This way a SmarkForm component of type input can be defined over either
<select>
, a<textarea>
or a<input>
tag with any valid value of its type property while, in the case of number, only a<input>
tag with a “number” as its type attribute is allowed (or with no type attribute in which case it will be auto-filled).And so on…
type: input
The 📋 input component type provide basic support for all HTML form fields (<input>
, <textarea>
and select
) no matter, in case of inputs its actual type attribute.
- Imports and exports String like regular HTML form fields.
Other component types may provide more advanced behaviour, like importing/exporting appropriate data types, for specific input types, etc… But every present and future HTML <input> tag could be used as SmarkForm “input” component type.
Example:
<div id="myForm">
<p>
<label data-smark>Name:</label>
<!-- Implicit (automatically inferred) component type: -->
<input type='text' name='name' data-smark>
</p>
<p>
<label data-smark>Surname:</label>
<!-- Explicitly specified component type: -->
<input type='text' name='surname' data-smark='{"type":"input"}'>
</p>
<p>
<label data-smark>User Name:</label>
<!-- Handy options-driven syntax: -->
<!-- 👉 type attribute ='text' is the default -->
<!-- 👉 {"type":"input"} inferred by tag name and type attribute -->
<input data-smark='{"name":"user_name"}'>
</p>
<p>
<label data-smark>Phone:</label>
<!-- Explicit better than implicit: -->
<!-- 👉 type='tel' is necessary here. -->
<!-- 👉 {"type":"input"} may prevent an hypotetical future "tel" -->
<!-- component type from being inferred here. -->
<input type='tel' data-smark='{"type":"input","name":"phone"}'>
</p>
<p>
<label data-smark>Address:</label>
<!-- Non <input> fields: -->
<textarea data-smark='{"type":"input","name":"address"}'></textarea>
</p>
<p>
<label data-smark>Email:</label>
<!-- Just another example: -->
<input type='email' data-smark='{"type":"input","name":"email"}'>
</p>
<p>
<button data-smark='{"action":"clear"}'>❌ Clear</button>
<button data-smark='{"action":"export"}'>💾 Submit</button>
</p>
</div>
const myForm = new SmarkForm(document.getElementById("myForm"));
/* Show exported data in an alert() window */
myForm.on("AfterAction_export", ({data})=>{
window.alert(JSON.stringify(data, null, 4));
});
The Singleton Pattern.
All Scalar field types implement the so called Singleton Pattern.
The Singleton Pattern consists in using a non-field html tag as a SmarkForm component of given scalar type.
👉 This allow for complex HTML to work as a single field.
👉 That HTML should contain one and only one HTML form field (<input>
, <textarea>
or <select>
).
This allow them to incorporate labels and triggers which is specially convenient for the list component type since it make possible to incorporate triggers for its actions in every list item even for lists of scalars.
Example:
<!-- ... -->
<li data-smark='{"type":"input"}'>
<input placeholder='Phone number' type='tel'/>
<button data-smark='{"action":"removeItem"}'>❌</button>
</li>
<!-- ... -->
See Applying the singleton pattern in the «list» Component Type chapter for a more real-world example.
The Singleton Pattern can also be used to avoid explicit context path specification for simple actions such as clear for field types like number or, specially, color (since native <input type='color'>
doesn’t allow not to specify any while SmarkForm component does.
Example:
👉 In the following example we need to explicitly specify the context path for the clear action since, otherwise, the whole form (its natural context) would be cleared.
<div id="myForm">
<p>
<p>
<label data-smark>Pick a Color:</label>
<input type="color" name="color" data-smark>
<button data-smark='{"action":"clear","context":"color"}'>❌ Reset</button>
</p>
</p>
<p>
<button data-smark='{"action":"clear"}'>❌ Clear</button>
<button data-smark='{"action":"export"}'>💾 Submit</button>
</p>
</div>
const myForm = new SmarkForm(document.getElementById("myForm"));
/* Show exported data in an alert() window */
myForm.on("AfterAction_export", ({data})=>{
window.alert(JSON.stringify(data, null, 4));
});
👉 Conversely, using the Singleton Pattern, not only the code looks clenaner but, also, it could avoid future issues in case of field name being changed (for instance after copying a block of code to reuse it somewhere else):
<div id="myForm">
<p>
<p>
<label data-smark>Pick a Color:</label>
<span data-smark='{"type":"color", "name":"bgcolor"}'>
<input data-smark>
<button data-smark='{"action":"clear"}'>❌ Reset</button>
</span>
</p>
</p>
<p>
<button data-smark='{"action":"clear"}'>❌ Clear</button>
<button data-smark='{"action":"export"}'>💾 Submit</button>
</p>
</div>
const myForm = new SmarkForm(document.getElementById("myForm"));
/* Show exported data in an alert() window */
myForm.on("AfterAction_export", ({data})=>{
window.alert(JSON.stringify(data, null, 4));
});
type: number and type: date
The 📋 number and 📋 date component type extends the Input component type providding extra sanitation (when importing) and formatting (when exporting).
-
number imports and exports Number.
-
date imports and exports Date.
-
Having they inherit from input, both implement the Singleton Pattern..
-
If inappropriate types (like String) are imported, they are properly converted on the fly.
Example:
<div id="myForm">
<p>
<label data-smark>Price:</label>
<input data-smark='{"type":"number","name":"price"}'>
</p>
<p>
<label data-smark>Date:</label>
<input data-smark='{"type":"date","name":"date"}'>
</p>
<p>
<button data-smark='{"action":"clear"}'>❌ Clear</button>
<button data-smark='{"action":"export"}'>💾 Submit</button>
</p>
</div>
const myForm = new SmarkForm(document.getElementById("myForm"));
/* Show exported data in an alert() window */
myForm.on("AfterAction_export", ({data})=>{
window.alert(JSON.stringify(data, null, 4));
});
type: color
Similar to number and date, the 📋 color component type extends the Input component type.
-
color imports and exports (Hex
#rrggbb
) string or Null. -
Invalid inputs are replaced by Null.
-
Having it inherits from input, it implements the Singleton Pattern..
However, unlike them, and as outlined in its specification, <input type="color">
HTML fields do not require additional sanitation or formatting.
This is because <input type="color">
ensures a consistently valid RGB color value and even if the user does not interact with the field, a valid value is always enforced.
As a result, it can be difficult to determine whether the user intentionally selected pure black (#000000) or simply overlooked the field altogether.
Example:
<div id="myForm">
<p>
<p>
<label data-smark>Pick a Color:</label>
<span data-smark='{"type":"color", "name":"bgcolor"}'>
<input data-smark>
<button data-smark='{"action":"clear"}'>❌ Reset</button>
</span>
</p>
</p>
<p>
<button data-smark='{"action":"clear"}'>❌ Clear</button>
<button data-smark='{"action":"export"}'>💾 Submit</button>
</p>
</div>
const myForm = new SmarkForm(document.getElementById("myForm"));
/* Show exported data in an alert() window */
myForm.on("AfterAction_export", ({data})=>{
window.alert(JSON.stringify(data, null, 4));
});
The clear action can be used to clear all other field component types
Example:
🗒️ HTML⚙️ JS👁️ Preview<div id="myForm"> <p> <p> <label data-smark>A Color:</label> <span data-smark='{"type":"color", "name":"color"}'> <input data-smark> <button data-smark='{"action":"clear"}'>❌ Reset</button> </span> </p> <p> <label data-smark>A Number:</label> <span data-smark='{"type":"number", "name":"number"}'> <input data-smark> <button data-smark='{"action":"clear"}'>❌ Reset</button> </span> </p> <p> <label data-smark>A Date:</label> <span data-smark='{"type":"date", "name":"date"}'> <input data-smark> <button data-smark='{"action":"clear"}'>❌ Reset</button> </span> </p> </p> <p> <button data-smark='{"action":"clear"}'>❌ Clear</button> <button data-smark='{"action":"export"}'>💾 Submit</button> </p> </div>
const myForm = new SmarkForm(document.getElementById("myForm")); /* Show exported data in an alert() window */ myForm.on("AfterAction_export", ({data})=>{ window.alert(JSON.stringify(data, null, 4)); });
type: select
The 📋 select compenent type will (🚧 since it is not yet implemented 🚧) provide support for advanced features like dynamic options loading and update, even reacting to changes of other fields in a really transparent manner thanks to the (future) “API interface”.
-
Will inmport and export array of String.
-
Will allow configuration to expor arrays of different types.
Non field component types
There are other component types with special mission that are not form field types:
type: trigger
The 🕹️ Triggers are used for buttons (or any other elements) to receive interaction events (mouse “click” by default, but they will be capable to handle others such as keyboard events) and trigger predefined actions on propper components (i.e. adding or removing items to lists).
- The good thing about triggers is that they usually don’t need any extra wiring to interact with their targeted components. They just target them by their own relative position in the SmarkForm form tree or, at most, with filesystem-like relative paths when necessary.
type: label
The 🏷️ Labels will (🚧 since it is not yet implemented 🚧) be used to enhance regular <label>
to properly resolve their for attribute from a SmarkForm relative path ensuring it always matches the propper id no matter if it is a root-level field or in the bottom of several subform and/or list nesting levels.