*************** foundation-form *************** ``foundation-form`` is an enhancement of
.. code-block:: html Event ===== foundation-form-submitted ------------------------- This event is triggered after the submission of the form is completely finished—i.e. when no further action is performed by ``foundation-form``. The event has the following parameters: .. code-block:: ts interface FoundationFormSubmittedEvent { /** * @param statustrue
when the form is successfully submitted; false
otherwise
* @param xhr The normalized jQuery XHR object
*/
(status: boolean, xhr: JQueryXHR): void;
}
AdaptTo Interface
=================
type
``foundation-form``
condition
``form.foundation-form``
returned type
``FoundationForm``
.. code-block:: ts
interface FoundationForm {
/**
* Submits the form asynchronously.
*
* Note that this is merely a convenience method to submit a form using Ajax.
* The behaviour of foundation-form is not performed by this method.
* Please submit the form by triggering the submit event normally to achieve that.
*
* @returns The promise of the response
*/
submitAsync(): Promise;
/**
* Resets the form. This method is intended to also reset foundation-field
in addition to standard form controls.
*
* @param skipNative Skips the native form reset method (HTMLFormElement#reset)
*/
reset(skipNative?: boolean): void;
/**
* Returns true
if the form is dirty; false
otherwise.
*
* Dirty form is when there is a change in the underlying fields by mean of foundation-field-change
event.
* And when the form is submitted successfully, the dirty state is reset to false
.
*/
isDirty(): boolean;
}
Response Handling
=================
``foundation-form`` provides a pluggable system to handle the form response.
The response is handled by the following steps:
#. The response is parsed by the matching response parser, which will return a parsing result (``ParsedData``).
#. If there is no matching parser, then jQuery default parsing behavior is used (e.g. ``application/json`` -> JS object; ``text/html`` -> string).
#. If the response is a success, ``ParsedData`` is passed to the matching success response handler and the handler is executed.
#. Likewise, if the response is an error, ``ParsedData`` is passed to the matching error response handler and the handler is executed.
Example
-------
Given the following markup:
.. code-block:: html
where the response of the markup is Sling' `HTMLResponse 200 |
Site created |
/content/geometrixx5 | /content/geometrixx5 |
jQuery.fn.is
.
*/
selector: string;
/**
* The regular expression to match the content type of the response,
* where the matching one will be handled by this parser.
*/
contentType: RegExp;
/**
* The handler to actually perform the action.
*
* If false
is returned, the next handler in the chain will be evaluated.
* Otherwise the chain stops and the return value is used as the parsed data.
*
* @param form The element of the .foundation-form
* @param xhr The normalized jQuery XHR object
* @param parsedResponse The result of parsing the response of the given xhr according to its content type (e.g. "text/html" => DocumentFragment).
* Use this instead of xhr.responseText
for performance.
*/
handler: (form: Element, xhr: JQueryXHR, parsedResponse: any) => any;
}
Example
-------
.. code-block:: js
/**
* Parser for Sling's HTMLResponse.class
.
*/
$(window).adaptTo("foundation-registry").register("foundation.form.response.parser", {
name: "foundation.sling",
selector: "*",
contentType: /text\/html/,
handler: function(form, xhr, parsedResponse) {
var frag = $(parsedResponse);
if (!frag.find("#Status").length) {
return false;
}
return Array.prototype.reduce.call(frag.find("[id]"), function(memo, v) {
if (v.href) {
memo[v.id] = v.href;
} else {
memo[v.id] = v.textContent;
}
return memo;
}, {});
}
});
Success Response Handler
========================
``foundation-form`` allows the success response to be pluggable. The handler is picked based on the matching name specified in the config at ``[data-foundation-form-response-ui-success]``.
The success response handler can be registered to the :doc:`registry <../registry/index>` using ``foundation.form.response.ui.success``
as the name and the config satisfying the following interface:
.. code-block:: ts
interface FoundationFormResponseUISuccess {
/**
* The name of the handler. It is recommended that it is namespaced using dot notation according to application that register the handler.
* "foundation" namespace is reserved for Granite UI.
*
* @example
* foundation.redirect
*/
name: string;
/**
* The handler to actually perform the action.
* If false
is returned, the next handler in the chain will be evaluated. Otherwise the chain stops.
*
* @param form The element of the .foundation-form
* @param config The value of [data-foundation-form-response-ui-success]
* @param data The result of parsing the raw response by FoundationFormResponseParser
* @param textStatus The status string from XHR
* @param xhr The normalized jQuery XHR object
* @param parsedResponse The result of parsing the response of the given xhr according to its content type (e.g. "text/html" => DocumentFragment).
* Use this instead of xhr.responseText
for performance.
*/
handler: (form: Element, config: FoundationFormResponseUISuccessConfig, data: any, textStatus: string, xhr: JQueryXHR, parsedResponse: any) => boolean;
}
/**
* The config of FoundationFormResponseUISuccess.
* The only required property is name
. The handler implementation is allowed to define any other property according to what it needs.
* Consult individual handler documentation.
*/
interface FoundationFormResponseUISuccessConfig {
/**
* The name of the handler to be performed when success response is returned by the server.
*/
name: string;
}
A chain of registered handlers is created using LIFO (last in, first out) algorithm.
The handler will be called when the name matches. If false is returned by the handler, the next handler in the chain will be evaluated. Otherwise the chain stops.
Example
-------
.. code-block:: js
/**
* Do a redirect to the given href.
*/
$(window).adaptTo("foundation-registry").register("foundation.form.response.ui.success", {
name: "foundation.redirect",
handler: function(form, config, data, textStatus, xhr, parsedResponse) {
var href = URITemplate.expand(config.href, data);
if (href && href[0] === "/") {
// only navigate when it starts with "/" to prevent open redirect
window.location = href;
}
}
});
Error Response Handler
======================
``foundation-form`` allows the error response to be pluggable.
Unlike success response, there is no markup associated with error response config.
This is due to the fact that error is usually unpredictable.
Rather each handler needs to inspect the response and decide to act upon it or simply pass it to the next handler.
The error response handler can be registered to the :doc:`registry <../registry/index>` using ``foundation.form.response.ui.error`` as the name and the config satisfying the following interface:
.. code-block:: ts
interface FoundationFormResponseUIError {
/**
* The handler to actually perform the action.
* If false
is returned, the next handler in the chain will be evaluated. Otherwise the chain stops.
*
* @param form The element of the .foundation-form
* @param data The result of parsing the raw response by FoundationFormResponseParser
* @param xhr The normalized jQuery XHR object
* @param textStatus A string describing the type of error that occurred (e.g. null
, "timeout", "error", "abort", "parsererror")
* @error errorThrown Usually the textual portion of the HTTP status, such as "Not Found" or "Internal Server Error"
*/
handler: (form: Element, data: any, xhr: JQueryXHR, textStatus: string, errorThrown: any) => boolean;
}
A chain of registered handlers is created using LIFO (last in, first out) algorithm. If false is returned by the handler, the next handler in the chain will be evaluated. Otherwise the chain stops.
Example
-------
.. code-block:: js
/**
* Show an alert showing the message from the response of Sling's HTMLResponse.class
.
*/
$(window).adaptTo("foundation-registry").register("foundation.form.response.ui.error", {
name: "foundation.sling",
handler: function(form, data, xhr, error, errorThrown) {
if (!data.Status) {
return false;
}
var title = Granite.I18n.get("Error");
var message = data.Message;
var ui = $(window).adaptTo("foundation-ui");
ui.alert(title, message, "error");
}
});
Submit Hook
===========
There is a pluggable hook to apply extra logic during submission.
The hook can be registered to the :doc:`registry <../registry/index>` using ``foundation.form.submit`` as the name and the config satisfying the following interface:
.. code-block:: ts
interface FoundationFormSubmit {
/**
* Only the element satisfying the selector will be handled by this parser.
* It will be passed to jQuery.fn.is
.
*/
selector: string;
/**
* The handler to actually perform the hook.
*
* This method is executed before the submission (i.e. it is a pre-submit hook).
* It can perform async operation, where the result is returned as a promise at FoundationFormSubmitResult#preResult
.
*
* @param form The element of the .foundation-form
*/
handler: (form: Element) => FoundationFormSubmitResult;
}
interface FoundationFormSubmitResult {
/**
* The result of pre-submit hook.
*
* The submission is blocked until all the returned promises of all registered hooks are fulfilled.
* When the promise is rejected, the submission is cancelled.
*/
preResult?: Promise;
/**
* The post-submit hook, which is executed after the submission.
*
* It can perform async operation, where the result is returned as a promise.
*
* The form subsequent logic is blocked until all the returned promises are fulfilled.
* Regardless of the result of the promise, the form logic will continue.
*/
post?: () => Promise;
}