«list» Component Type
📖 Table of Contents
Introduction
The List component in SmarkForm allows you to dynamically manage a list of items within your form.
👉 All lists direct children (before rendering) are considered templates with different roles.
👉 Default role is “item”, which is used as a template for each item in the list. This template is mandatory.
👉 Other available roles are:
empty_list: Displayed when the list is empty (removed when items are added).header: Persistent header element prepended once to the list. Can contain triggers but not SmarkForm fields.footer: Persistent footer element appended once to the list. Can contain triggers but not SmarkForm fields.separator: Template cloned between each pair of adjacent items (removed when only one item remains).last_separator: Likeseparatorbut used only before the last item. Falls back toseparatorif not provided.placeholder: Visual placeholder slot used only whenmax_itemsis set. One placeholder is shown for each slot not yet occupied by a real item.
👉 Likewise forms, list inputs can be created over any HTML tag except for actual HTML form field elements (<input>, <textarea>, <select>, <button>…).
List items
👉 Lists can contain a variable number of unnamed inputs (list items) of a given type.
👉 However, in its html source, lists must only contain templates of supported roles as direct children , being the “item” role required and the rest optional.
👉 The user will (or won’t) be able to, at its own discretion (and according certain configurable rules), add or remove items to the list.
👉 Every time a new item is added to the list, its item template is automatically rendered as a SmarkForm field (no matter if we explicitly specified the data-smark attribute or not).
👉 If data-smark attribute is not provided (or it does not specify the type property), the type “form” is automatically taken by default .
Example:
<div id="myForm">
<section data-smark='{"type":"list","name":"users"}'><!-- ☛ 1 -->
<fieldset style="text-align:right"><!-- ☛ 2, 3, 6 -->
<p><label data-smark>User name:</label><input name='name' type='text' data-smark/></p>
<p><label data-smark>Phone number:</label><input name='phone' type='tel' data-smark/></p>
<p><label data-smark>Email:</label><input name='email' type='text' data-smark/></p>
<button data-smark='{"action":"removeItem"}' title='Remove User'>➖</button>
</fieldset>
</section>
<button data-smark='{"action":"addItem","context":"users"}' title='Add User'>➕</button>
</div>
/* Make disabled buttons more evident to the eye */
button:disabled {
opacity: .5;
}
const myForm = new SmarkForm(document.getElementById("myForm"), {
value: { "users": [ {"name": "Alice Johnson", "phone": "+1 555 234 5678", "email": "alice.j@example.com"}, {"name": "Bob Smith", "phone": "+1 555 876 4321", "email": "bob.smith@example.com"} ] }
});
👉 With exportEmpties option set to false (default), lists won’t export empty items.
👉 …unless there is no enough non empty items to satisfy minItems option, in which case up tu minItems empty items will be exported.
Every example in this section comes with many of the following tabs:
- HTML: HTML source code of the example.
- CSS: CSS applied (if any)
- JS: JavaScript source code of the example.
- Preview: This is where you can see the code in action.
- Notes: Additional notes and insights for better understanding. Don't miss it‼️
✨ Additionally, in the Preview tab, you will find handy buttons:
⬇️ Exportto export the form data to the JSON data viewer/editor.⬆️ Importto import data into the form from the JSON data viewer/editor.♻️ Resetto reset the form to its default values.❌ Clearto reset the form to its initial state.
Scalar item types
👉 Other field types can be used too as item template .
👉 …but, in the case of (scalar field types) it may look like we are limited when it comes to inserting labels and triggers in the item template and hence we can only remove last item every time in the list.
This would force us to move the Remove Item button outside the list like in the following example.
Example:
<div id="myForm">
<section style="display:grid" data-smark='{"type":"list","name":"phones"}'>
<input placeholder='Phone number' type='tel'/><!-- ☛ 4, 6 -->
</section>
<button data-smark='{"action":"addItem","context":"phones"}' title='Add Phone'>➕</button>
<button data-smark='{"action":"removeItem","context":"phones"}' title='Remove Phone'>➖</button> <!-- ☛ 5 -->
</div>
/* Make disabled buttons more evident to the eye */
button:disabled {
opacity: .5;
}
const myForm = new SmarkForm(document.getElementById("myForm"), {
value: { "phones": ["+1 555 100 2000", "+1 555 200 3000"] }
});
Every example in this section comes with many of the following tabs:
- HTML: HTML source code of the example.
- CSS: CSS applied (if any)
- JS: JavaScript source code of the example.
- Preview: This is where you can see the code in action.
- Notes: Additional notes and insights for better understanding. Don't miss it‼️
✨ Additionally, in the Preview tab, you will find handy buttons:
⬇️ Exportto export the form data to the JSON data viewer/editor.⬆️ Importto import data into the form from the JSON data viewer/editor.♻️ Resetto reset the form to its default values.❌ Clearto reset the form to its initial state.
Notice that in this example, likewise the fieldset in the former, the input tag has no “name” attribute . This is because it is a list item template and it’s actual name attribute will be automatically set depending on its position in the array every time a new item is added, moved or removed.
👉 Now, when the user clicks the Remove Item button, it will default to the last item of the list, but we cannot (yet) cherry-pick which item we’d like to remove.
Applying the singleton pattern
Thankfully, all Scalar field types implement the Singleton Pattern so that we can use any other html tag in place and just put the form field tag inside.
Example:
<div id="myForm">
<ul data-smark='{"name": "phones", "of": "input", "max_items": 3}'>
<li>
<input placeholder='Phone Number' type="tel" data-smark>
<button data-smark='{"action":"removeItem"}' title='Remove Phone'>➖</button>
</li>
</ul>
<button data-smark='{"action":"addItem","context":"phones"}' title='Add Phone'>➕</button>
</div>
/* Make disabled buttons more evident to the eye */
button:disabled {
opacity: .5;
}
const myForm = new SmarkForm(document.getElementById("myForm"), {
value: { "phones": ["+1 555 100 2000", "+1 555 200 3000"] }
});
Every example in this section comes with many of the following tabs:
- HTML: HTML source code of the example.
- CSS: CSS applied (if any)
- JS: JavaScript source code of the example.
- Preview: This is where you can see the code in action.
- Notes: Additional notes and insights for better understanding. Don't miss it‼️
✨ Additionally, in the Preview tab, you will find handy buttons:
⬇️ Exportto export the form data to the JSON data viewer/editor.⬆️ Importto import data into the form from the JSON data viewer/editor.♻️ Resetto reset the form to its default values.❌ Clearto reset the form to its initial state.
In this example we have omitted the
type: "list"bit and still works because SmarkForm automatically inferes the type from the HTML tag.This is handy for fast developping but it is not a recommended practice since our designer may decide to change the tag for the template and different type could be infered.
Nesting lists
Since they’re just smarkform fields, lists can be nested as needed.
Now we are prepared to extend our initial Users list example by providding, say, up to three phone numbers and up to three emails.
Example:
<div id="myForm">
<section data-smark='{"type":"list","name":"users"}'>
<fieldset>
<legend>User</legend>
<button data-smark='{"action":"removeItem"}' title='Remove User'>➖</button>
<input name='name' placeholder='User name' type='text' data-smark/>
<fieldset>
<legend>
<span
data-smark='{"action":"addItem","context":"phones"}'
title='Add Phone'
style="background: lightgray; padding:.3em; border-radius:3px; margin: .4em"
>➕</span>
Phone Numbers
</legend>
<ul data-smark='{"type": "list", "name": "phones", "of": "input", "max_items": 3}'>
<li>
<input type="tel" data-smark>
<button data-smark='{"action":"removeItem"}' title='Remove Phone'>➖</button>
</li>
</ul>
</fieldset>
</fieldset>
</section>
<button data-smark='{"action":"addItem","context":"users"}' title='Add User'>➕ Add user</button>
</div>
/* Make disabled buttons more evident to the eye */
button:disabled {
opacity: .5;
}
const myForm = new SmarkForm(document.getElementById("myForm"), {
value: { "users": [ {"name": "Alice Johnson", "phones": ["+1 555 234 5678", "+1 555 876 4321"]} ] }
});
Every example in this section comes with many of the following tabs:
- HTML: HTML source code of the example.
- CSS: CSS applied (if any)
- JS: JavaScript source code of the example.
- Preview: This is where you can see the code in action.
- Notes: Additional notes and insights for better understanding. Don't miss it‼️
✨ Additionally, in the Preview tab, you will find handy buttons:
⬇️ Exportto export the form data to the JSON data viewer/editor.⬆️ Importto import data into the form from the JSON data viewer/editor.♻️ Resetto reset the form to its default values.❌ Clearto reset the form to its initial state.
As you can see here, phones and emails lists share almost the same layout.
Since SmarkForm works just over the DOM, you can use your preferred HTML templating system. For instance, here you can see a similar mixin implemented whith Pug templates.
Also it is planned to implement a mixin feature allowing to create SmarkForm components from SmarkForm html code.
For more information on using the List component and its available methods, please refer to the API Reference.
API Reference
Options
min_items
Establishes the minimum number of items allowed.
- Type: Number
- Default value: 1
- Minimum value: 0
max_items
Establishes the maximum number of items allowed.
- Type: Number
- Default value: Infinity
- Minimum value: Infinity
sortable
Controls wether the list can be user sorted by dragging and dropping list items.
- Type: Boolean
- Default value: false
Drag and Drop events are not natively supported by touch devices.
They can be emulated in serveral ways. A quite straighforward one is through the dragdroptouch library from Bernardo Castilho:
exportEmpties
Controls whether unfilled list items should be exported or not. This allows for neater arrays when the user adds more items to the list than are used.
- Type: Boolean
- Default value: false
of
Specify a field type for list items. Handy to avoid specifying a whole data-smark attribute in the template to just specify the field type when needed.
- Type: string
- Default value: undefined
Actions
Actions are special component methods that can be triggered both programmatically (by directly calling the function) or by user interaction with a trigger component. Each action is associated with a specific behavior that can be performed on the component.
👉 Actions receive an
optionsobject as an argument, which contains the necessary information to perform the action.👉 When called from triggers, the properties defined in their
data-smarkobject, are passed asoptionsto the action. Those expecting a path(★ ) to other components are resolved to the actual component.
The list component type supports the following actions:
(Async) export (Action)
Options (export)
- action: (= “export”)
- origin: Origin is automatically set to the trigger component that called the action (and cannot be overridden). In case of a manual call, it is set to Null.
- context: Path★ (absolute or relative to its natural context) to the component that is the context of the action. If not provided, the context is set to its natural context.
- target: Path★ (absolute or relative to its context). Pipes the exported data to the import action of the target component.
(Async) import (Action)
Options (import)
- action: (= “import”)
- origin: Origin is automatically set to the trigger component that called the action (and cannot be overridden). In case of a manual call, it is set to Null.
- context: Path★ (absolute or relative to its natural context) to the component that is the context of the action. If not provided, the context is set to its natural context.
- target: Path★ (absolute or relative to its context). Pipes the imported data from the export action of the target component, overridding the “data” option if specified.
- data: (array / any¹)
- focus: (boolean, default true)
- setDefault: (boolean, default
true) — Whentrue(the default), the imported data becomes the new default restored byreset(). Passfalseto import data without changing the reset target.
¹) If non array value is provided as data, then it is automatically wrapped as such as a failback.
(Async) clear (Action)
Clears the list to an empty array, removing all items regardless of any configured default values. Unlike reset, this action ignores any prepopulated default items that may have been set via the value option.
Example use case: A “Clear All” button that removes all items from the list.
Options (clear)
- action: (= “clear”)
- origin: Origin is automatically set to the trigger component that called the action (and cannot be overridden). In case of a manual call, it is set to Null.
- context: Path★ (absolute or relative to its natural context) to the component that is the context of the action. If not provided, the context is set to its natural context.
(Async) reset (Action)
Reverts the list to its default structure. The default is initially set by the value option, and is updated every time import() is called with setDefault: true (the default). If no default has ever been set, the list reverts to an empty array (same as clear).
Example use case: A “Reset” button that restores the list to its last loaded state.
Options (reset)
- action: (= “reset”)
- origin: Origin is automatically set to the trigger component that called the action (and cannot be overridden). In case of a manual call, it is set to Null.
- context: Path★ (absolute or relative to its natural context) to the component that is the context of the action. If not provided, the context is set to its natural context.
(Async) addItem (Action)
Options (addItem)
- action: (= “addItem”)
- origin: Origin is automatically set to the trigger component that called the action (and cannot be overridden). In case of a manual call, it is set to Null.
- context: Path★ (absolute or relative to its natural context) to the component that is the context of the action. If not provided, the context is set to its natural context.
- target: Path★ (absolute or relative to its context) to a component to be used as a base reference to calculate the position of the new item. If not provided, the last item in the list will be used.
- position: (= “after” (default) / “before”) Determines where the new item will be inserted in relation to the target.
- source: (Path★ (absolute or relative to the newly created item). If provided, the matched component value (result of its export action) will be imported to the new item.
- autoscroll:, = “elegant” / “self” / “parent” / falsy
- failback: (= “none” / “throw” (default)) Avoid emitting the “LIST_MAX_ITEMS_REACHED” event when the maximum number of items is reached.
(Async) removeItem (Action)
Options (removeItem)
- action: (= “removeItem”)
- origin: Origin is automatically set to the trigger component that called the action (and cannot be overridden). In case of a manual call, it is set to Null.
- context: Path★ (absolute or relative to its natural context) to the component that is the context of the action. If not provided, the context is set to its natural context.
- target: Path★ (absolute or relative to its context) to the component to be removed. If not provided, the last item in the list will be used.
- autoscroll: (= “elegant” / “self” / “parent” / falsy)
- preserve_non_empty: (boolean)
- failback: (= “none” / “clear” / “throw” )
count (Action)
Options (count)
- action:: (= “count”)
- delta:: (default 0)
position (Action)
Options (position)
- action:: (= “position”)
- offset:: (default 1)