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.

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 TEXTAREAs 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:

  1. The hyperlink will be handled by Sircl (since it has an inline target)
  2. The hyperlink has the substitute-fields class
  3. 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.

 

 

Loading...