Forms
Forms are crucial in web site and applications to allow the user to communicate with the server. Sircl includes features that make developing forms easier and provides a better interaction with the user.
Form initialization
Sircl provides convenience operations on forms that sets certain form fields in initial state without the need for server-side coding.
Initializing fields from query
Form fields can be initialized with a value from the query string by means of the onload-setvaluefromquery
class or attribute:
<input type="text" name="FirstName" class="onload-setvaluefromquery" />
In this case, the field gets initialized with the value of a "FirstName" query string parameter.
If the name of the query string attribute does not match the name of the field, the onload-setvaluefromquery
attribute can be used with as value the query string parameter name to use:
<input type="text" name="FirstName" onload-setvaluefromquery="fn" />
Here the field gets initialized with the value of a "fn" query string parameter.
Note that initializing a field with the onload-setvaluefromquery
class or attribute triggers a change event. To avoid this change event, either add an onchange-propagate
attribute with the value "off" on the field, or replace the onload-setvaluefromquery
attribute on the field by a value
attribute with the proper value.
Initializing SELECTs
To set the value of a SELECT
element on initialization (when the page (part) is loaded), you can use the onload-defaultselect
attribute.
The attribute recognizes the special value ":singleton" which selects the option with a non-empty value provided there is only one such options:
<select onload-defaultselect=":singleton" required>
<option value="">Choose a size</option>
<option value="XL">Extra Large</option>
</select>
Alternatively, the special value ":first" will make the first non-empty option to be selected:
<select onload-defaultselect=":first" required>
<option value="">Choose a size</option>
<option value="S">Small</option>
<option value="L">Large</option>
</select>
Any other attribute value will make the option with the given attribute value to be selected:
<select onload-defaultselect="B" required>
<option value="">Select a region</option>
<option value="A">Region A</option>
<option value="B">Region B</option>
</select>
The onload-defaultselect
attribute only changes the selected option of a SELECT
element if it has no option selected yet, or if the selected option has an empty value.
Note that setting the value of a SELECT
element by means of the onload-defaultselect
attribute triggers a change event. To avoid this change event, either add an onchange-propagate
attribute with the value "off" on the SELECT
element, or replace the onload-defaultselect
attribute on the SELECT
element by a selected
attribute on the option to be selected.
Changed state management
Changed state detection
When a form has an onchange-set
attribute with as value the name of a field, that field will receive the value "true" when a change event is triggered on the form. In addition, the form will get the form-changed
class. The form also gets the class form-changed
when loaded, if its field named by the onchange-set
attribute has an initial value of "true" or "on" (case insensitive check).
Example:
<form onchange-set="HasChanges">
<input type="hidden" name="HasChanges" />
<input type="text" name="Town" />
<button type="submit">Submit</button>
</form>
When the value of the Town field changes, the value of the HasChanges field changes to "true" and the form gets the form-changed
class.
If the form roundtrips to the server and comes back with still "true" as value of the HasChanges field, then the form-changed
class is automatically re-applied to the form.
If the form roundtrips to the server but returns only a part of the form that does not include the HasChanges fields, then the form can be marked changed by including a response header X-Sircl-Form-Changed
with the value "true":
X-Sircl-Form-Changed: true
Note that change events triggered during page (part) initialization such as those generated by the onload-setvaluefromquery
and onload-defaultselect
attributes, do not mark the form as changed.
Another way to mark a form changed is by clicking an element inside the form that has the onclick-setchanged
class, or by clicking an element anywhere on the page that has an onclick-setchanged
attribute with as value a CSS selector refering to the form.
Ignoring change state changes
When change state detection is in use, all form fields triggering a change event will cause the form to be marked as changed. To exclude specific fields from causing the form to be marked as changed, add an onchange-ignore
class to those fields or to a (common) parent element. Use this for instance for non-persisted checkboxes and radios that impact the view state but not the data state such as for instance a checkbox indicating whether or not advanced options are visible.
Changed state confirmation
When a form contains changes, and the user clicks on any hyperlink in or outside the form, a new page is loaded and the changes in the form get lost.
To protect agains this loss, user confirmation can be requested by means of a confirm dialog. This is done by setting an onunloadchanged-confirm
attribute on the form(s) to be protected, with as value the message to show when a hyperlink is clicked while the form is stale.
To allow clicking on a hyperlink even when a form has changes, add the onunloadchanged-allow
class on the hyperlink, as in the second hyperlink in this example:
<a href="/Elsewhere">Leave page</a>
<a href="/Elsewhere" class="onunloadchanged-allow">Leave page without confirmation</a>
...
<form onchange-set="HasChanges" onunloadchanged-confirm="Ok to loose changes ?">
<input type="hidden" name="HasChanges" />
<input type="text" name="Town" />
<button type="submit">Submit</button>
</form>
If multiple forms have changes, confirmation will be asked for the first one only.
Inversely, if confirmation is requested when clicking an element (hyperlink or button) on a form containing changes, set the onclickchanged-confirm
attribute with as value the confirmation message to show:
<form onchange-set="HasChanges">
<input type="hidden" name="HasChanges" />
<input type="text" name="Town" />
<button type="submit" onclickchanged-confirm="Are you sure you want to save those changes ?">Submit</button>
</form>
Input event handling
Triggering change on input
Textual INPUT
controls and TEXTAREA
s raise a change event when their value is changed and the control loses focus.
To detect changes earlier, on input, you can use the oninput-change
class. Any INPUT
or TEXTAREA
control having this class will raise a change event when input was given, and input was paused for 0.8 seconds. This "idle" delay of 0.8 seconds is there to avoid flooding the server with requests if a change event is automatically translated into a server request such as when an onchange-submit
attribute is used.
In the following example, when the use types in the name of the town, the form switches to changed state even before leaving the town field:
<style>
.form-changed { background-color: yellow; }
</style>
<form onchange-set="HasChanges">
<input type="hidden" name="HasChanges" />
<input type="text" name="Town" class="oninput-change" />
<button type="submit">Submit</button>
</form>
Instead of the oninput-change
class, you can also use the oninput-changeafter
attribute taking as value the requested delay (expressed in seconds).
Submitting forms
Submitting on change
Sometimes, when a control value changes, the view of the form should change. New controls should be visible, others hidden, enabled or disabled, etc. The Sircl library relies on the server to render views, and that means that when a control value changes, that requires a view change, a roundtrip to the server is required.
This roundtrip is done through submitting the form. To submit the form automatically on change (without requiring the user to press a submit button), use the onchange-submit
attribute or class.
The onchange-submit
class can be used on a control, a parent element (i.e. a FIELDSET
element grouping multiple controls) or the FORM
element, as the change event bubbles up the document structure.
The following form allows entering customer data:
<form action="/AddCustomer" method="post" class="target">
<fieldset class="onchange-submit">
<legend>Customer type</legend>
<label><input type="radio" name="CType" value="C" checked> Company</label>
<label><input type="radio" name="CType" value="I"> Individual</label>
</fieldset>
<fieldset>
<legend>Name</legend>
<input type="text" name="CName" placeholder="Company Name" />
<br />
<label>
<input type="checkbox" name="HasVat" value="True"
class="onchange-submit"
formaction="/VatInfo"
formtarget="#vatinfo">
Has a VAT number
</label>
<div id="vatinfo"></div>
...
</fieldset>
<button type="submit" formaction="/SaveCustomer"> Save </button>
</form>
Because of the onchange-submit
class on the fieldset line 2, when the user switches between Company and Individual, the whole form is submitted and replaced with a new view provided by the server. Because of the target
class on the FORM
element, the server should only provide new content for inside the FORM
element.
This way, the server can render a different view for individual customers (asking for first name, lastname, etc), and for company customers (asking for company name, VAT, etc).
In this form, companies do not necessarily have VAT numbers. The checkbox on line 12 allows the user to select whether the company has a VAT number or not. If this checkbox is changed - as it also has the onchange-submit
class - the form is submitted. But while the whole form is submitted (the server will have access to all the data entered in the form), because of the formaction
attribute, it is not the "/AddCustomer" action that will be posted, but the "/VatInfo" action. And because of the formtarget
attribute, the server should only provide new content for the #vatinfo element.
onchange-submit
can also appear as attribute, in which case it's value is a CSS selector refering to the form to be submitted, allowing another form to be submitted than the current one.
Also, while a zone can be created where all inner controls will trigger a form submit on change, using the onchange-submit
class/attribute, it is also possible to exclude a subset of elements using the onchange-nosubmit
class on them or any of their parent elements.
The onchange-submit
class/attribute is key in Sircl, as it provides support for server-side re-rendering of forms when data is changed.
In some cases, the ifchecked-*
, ifunchecked-*
and ifvalue*-*
event-actions defined in the extended libary (sircl-extended.js) can provide an alternative way to alter the view on change without the need to send a request to the server.
Submitting on input
While there is no direct way to translate an input event into the submission of a form, both classes oninput-change
and onchange-submit
can be combined to trigger a form submit on input (with an idle delay of 0.8 second by default).
The following example uses this combination to simulate an auto-complete controlled by the server:
<form class="onchange-submit" action="/AcCustomers" target="#custlist" >
<input class="oninput-change" type="text" name="Q" list="custlist" autocomplete="off" />
<datalist id="custlist"></datalist>
</select>
The autocomplete="off"
attribute on the INPUT
element disables the browsers auto-complete feature. The element also has a DATALIST
. And though the list is initially empty, when the user types in a value, the oninput-change
class will raise a change event. The onchange-submit
class on the form will then submit the form, providing access to the server to the entered text. The form has a target
attribute instructing the insert the response of the server into the datalist.
Change-actions also allow implementing an autoc-complete feature. But without submitting the whole form. This is usually a better approach.
Default submit buttons
Using the onkeyenter-click
and onkeyescape-click
attributes on FORM
elements allow you to define a default button (that is clicked when pressing the ENTER key in an INPUT
element) and a cancel button (that is clicked when pressing the ESCAPE key in an INPUT
element). The value of these attributes is a CSS selector refering to the element to be clicked:
<form enkeyenter-click="#btnOk" onkeyescape-click="#btnCancel" >
<input type="text" name="Name" autocomplete="off" />
<button id="btnOk" type="submit"> OK </button>
<a id="btnCancel" href="history:back" onclick-confirm="Quit?"> Cancel </a>
</form>
In this example, the default cancel button is a hyperlink. The default submit and default cancel buttons can be any element on which a click event can be sent.
Disabling on submit
The onsubmit-disable
class, to be used on FORM
elements, will make all submit elements (submit buttons) of the form disabled during the form submission process. And re-enables them afterwards. This provides a security against "double-clicking" or too fast clicking of submit buttons making the form being reposted faster than it can be processed.
Example:
<form class="onsubmit-disable">
<input type="text" name="Name" />
<button type="submit"> Submit </button>
</select>
To disable not only, or not all submit elements, use the onsubmit-disable
attribute, in which the value is a CSS selector refering to the elements to disable.
In the following example, all input controls and buttons inside the form are disabled during the posting of the form:
<form onsubmit-disable=">BUTTON, >INPUT, >SELECT, >TEXTAREA">
<input type="text" name="Name" />
<button type="submit"> Submit </button>
</select>
Notice how relative CSS selectors are used to refer to elements inside the current form only.
Confirmation on submit
The onsubmit-disable
attribute, holding a confirmation message, will trigger a confirmation request before submitting the form.
<form onsubmit-confirm="Are you sure ?">
<input type="text" name="Name" />
<button type="submit"> Submit </button>
</select>
The onsubmit-confirm
attribute can be placed on the submit trigger (button) or on the form.
Field substitution
Hyperlinks inside (or outside) forms can have field substitutions: parts of the URL get their values from fields in the form the link is in. If the hyperlink is not inside a form, matching fields are searched (by name) accross the whole document.
For field substitution to work, the hyperlink element (any element with a href
, onclick-load
or onload-load
attribute that is handled by Sircl (performs a page part request)) must also have the class substitute-fields
. The action URL can then contain references to other fields by placing their name between square brackets ([ and ]).
In the following example, the hyperlink will retrieve information about the Orange, as all three conditions are met:
- The hyperlink will be handled by Sircl (since it has an inline target)
- The hyperlink has the
substitute-fields
class - The hyperlink contains a substitution field "[FruitId]" which will be replaced by the current value of the FruitId field in the form.
<form>
<select name="FruitId">
<option value="1">Apple</option>
<option value="2" selected>Orange</option>
<option value="3">Pineapple</option>
</select>
<a class="substitute-fields" href="/GetFruit?id=[FruitId]" target="#fruitInfo">Get fruit</a>
<p id="fruitInfo"></p>
</form>
The invoked URL will be:
/GetFruit?id=2
When more than one matching field is found, only the value of the first one will be substituted.
Miscellaneous
Selecting text
To have all text of an INPUT
control selected when it gains the focus, an onfocus-select
class can be placed on the control or any of it's parents.
When the onfocus-select
attribute is placed on a parent element of (one or) more controls, all controls in scope will have their values selected when gaining the focus. This behavior can be disabled for individual controls in the scope by decorating them with the onfocus-noselect
class.
Note that the onfocus-select
attribute is part of the extended library.
Trimming values
To have on INPUT
control text trimmed (spaces at start and end removed), place an onfocusout-trim
class on the control or any of it's parents. The value of the control (or controls nested in the decorated element) will then be trimmed when the focus leaves the control provided the value of the control was changed.
When the onfocusout-trim
attribute is placed on a parent element of (one or- more controls, all controls in scope will have their values trimmed on change. This behaviour can be disabled for individual controls in the scope by decorating them with the onfocusout-notrim
class.
Note that the onfocusout-trim
attribute is part of the extended library.