Ajax#
Overview#
Treibstoff provides an Ajax mechanism to integrate Server-Side Rendering (SSR) into Single-Page Applications (SPA), driven by XML-Attributes on the HTML markup.
Therefor a set of Ajax operations is provided. The actual Server-Side Rendering
is done by an endpoint named ajaxaction, which gets called with information
provided via the Ajax related DOM attributes.
XML Namespace#
Ajax related attributes are defined in its own XML namespace.
Define this namespace on the HTML document:
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:ajax="http://namespaces.conestack.org/ajax">
</html>
Bind Operations#
Ajax operations are bound to DOM elements by adding ajax:bind attribute
containing a space separated list of event names to bind. These can be
DOM events or any kind of custom event:
<a ajax:bind="click">
Click me!
</a>
All following Ajax related DOM attributes have no effect (except ajax:form,
see Forms), without getting bound to one or more events.
Define a Target#
The ajax:target attribute contains the Server Target on which the
ajaxaction is called, and consists of the base URL and may include query
parameters which get passed to the server:
<a ajax:bind="click"
ajax:target="https://tld.com/resource?param=value">
Click me!
</a>
The target in this example results in in URL
https://tld.com/resource/ajacaction?param=value. The user needs to make
sure to provide the appropriate endpoint on the server, e.g. via URL dispatching
or traversal.
Perform Actions#
The actual Server-Side Rendering is triggered by defining the ajax:action
attribute. It contains a colon seperated triple with the action name, a
selector which identifies the DOM element to modify with the response and
a DOM modification mode:
<a ajax:bind="click"
ajax:action="rendersomething:.#element-id:replace"
ajax:target="http://tld.com?param=value">
Click me!
</a>
Now, when the link gets clicked, the DOM element with id #element-id is
replaced by the results of action rendersomething. The server target is
taken from ajax:target attribute.
Trigger Events#
When adding ajax:event attribute, an event gets triggered to all elements
defined by selector. The event instance provides the defined target.
<a ajax:bind="click"
ajax:event="update_something:.something-tp-update"
ajax:target="http://tld.com/resource?param=value">
Click me!
</a>
This causes update_something event being triggered on all DOM elements with
something-to-update CSS class set when the link gets clicked. The target
gets written on the event at property ajaxtarget.
This feature is useful when providing actions which can be triggered from several places in the application. The event receiving DOM element contains the action definition:
<div id="#something"
class="something-to-update"
ajax:bind="update_something"
ajax:action="rendersomething:#something:replace">
</div>
If binding actions which get triggered by Ajax event operations, there’s no need to define the target as it gets passed along with the event.
Overlays Actions#
The overlay operation is a special kind of action operation, which renders
the result of the action into an overlay. This is achieved by defining
ajax:overlay attribute on the DOM element.:
<a ajax:bind="click"
ajax:target="http://tld.com/some/path?param=value"
ajax:overlay="rendersomething">
Click me!
</a>
Other than ajax:action, the value of ajax:overlay contains only the
action name, since selector and mode are implicit due to the use
of the Overlay widget.
Overlays can be closed by setting special value CLOSE to ajax:overlay,
colon seperated followed by the overlay UID, which gets passed as
ajax.overlay-uid request parameter to the server:
<a ajax:bind="click"
ajax:overlay="CLOSE:12345">
Click me!
</a>
Confirming Operations#
By defining ajax:confirm attribute on the DOM element, a confirmation
dialog gets displayed with the value of this attribute as dialog text, and
executes the operation only if th user confirms it:
<a ajax:action="rendersomething:.#something:replace"
ajax:target="http://tld.com/some/path?param=value"
ajax:confirm="Do you really want to do this?">
Click me!
</a>
Browser History#
To provide a sane browser history, ajax:path and related attribute are
provided. The path operation causes Ajax operation definitions to be written
to the browser’s session history stack. The path operation listens to the
window’s popstate event and executes the Ajax operations contained in state if
any.
Treibstoff not provides a client side SPA routing mechanism. If the URL under path is supposed to display the same contents as the outcome of the Ajax operations when entered directly in the URL bar, the server side is responsible to render it accordingly.
It is totally sane to use the history stack in your own Jacascript as long as
the pushed state objects not contains a property named _t_ajax, which is
used to detect Ajax operations on popstate. Also make sure to unbind only
custom popstate handles from window on cleanup to avoid breaking Ajax history
handling.
How the path is extracted from ajax:path follow these rules:
When value is set to
target, path gets extracted fromajax:targetattribute including request parameters. This is the most common use.When value is set to
hrefand DOM element is a link, path gets taken from there.When setting it to a dedicated path, e.g.
/some/path, it is used as defined. This is in particular useful if the operation target contains request parameters but the path should not.
The following example add an Ajax action operation to the browser histroy stack:
<a ajax:bind="click"
ajax:target="https://tld.com/some/path?param=value"
ajax:path="target"
ajax:path-action="rendersomething:#something:replace">
Click me!
</a>
For a full documentation about the path operation related attributes, see
AjaxPath docs.
Operation Execution Order#
When defining multiple Ajax operations on a single DOM element, they get executed in the following order:
If a confirmation disalog is requested, it gets displayed first.
Action operation gets dispatched if defined.
Event operation gets dispatched if defined.
Overlay operation gets dispatched if defined.
Path operation gets dispatched if defined.
Ajax Forms#
Forms can be “ajaxified” by adding either ajax:form attribute or the CSS
class ajax to the form DOM element. The value of ajax:form can
be arbitrary:
<form ajax:form="true"
id="someform"
method="post"
action="http://example.com/some/path/formendpoint"
enctype="multipart/form-data">
</form>
Unlike the other operations, Ajax forms are no “real” Ajax operation in terms
of the implementation. Under the hood, Ajax forms are posted to a hidden
iframe, and a defined response format is expected from the server
implementation processing the form. This approach is chosen to avoid common
problems with file uploads and Ajax forms.
The form action URL gets called as is, it’s up to the user to provide the
Server implementation rendering the expected response.
See Server Side Documentation for details how to proper implement Ajax form responses.
Server Side#
Treibstoff not provides any SSR implementation. It’s up to the user to implement the required enpoints on the server.
Ajax Action#
When executing Ajax actions, a JSON request gets send to the ajaxaction
endpoint on server target (see ‘Define Target’).
The following request parameters are passed (additional to the one defined on the action target):
ajax.actionName of the requested action.
ajax.selectorDOM element selector for action. It must be added to response response. Can be
NONE, which means that no markup is manipulated after action. This is useful in combination with continuation operations.
ajax.modeThe DOM manipulation mode. Either
innerorreplaceorNONE(see above).
ajax.overlay-uidThis parameter gets additionally set if performing an overlay operation.
The endpoint is must return the requested resource as a JSON response in the follow inf format:
{
mode: 'inner', // the passed mode
selector: '#someid', // the passed selector
payload: '<div>...</div>', // markup rendered by the action
continuation: [{}], // continuation operations
}
Continuation Operations#
The server side may include continuation Ajax operation which gets executed
immediately after the client received the response from ajaxaction,
modified the DOM tree and rebound the response payload.
This is useful if reloading or updating of UI components depends on the completion of an ajax action (e.g. deleting a resource) or for closing overlays (e.g. if overlays are used for rendering ajax forms).
The continuation property of the ajaxaction reponse contains an array
of operation definitions to execute.
To execute an action operation on continuation, add an object defining:
{
'type': 'action',
'target': 'http://tld.com',
'name': 'actionname',
'mode': 'inner',
'selector': '.foo'
}
To execute an event operation on continuation, add an object defining:
{
'type': 'event',
'target': 'http://tld.com',
'name': 'eventname',
'selector': '.foo',
'data': {}
}
The data property gets set on the event instance on client side and can be
used to pass additional data to custom event handlers.
To execute a path operation on continuation, add an object defining:
{
'type': 'path',
'path': '/some/path',
'target': 'http://tld.com/some/path',
'action': 'actionname:.selector:replace',
'event': 'eventname:.selector',
'overlay': 'actionname',
'overlay_css': 'someclass',
'overlay_uid': '1234',
'overlay_title': 'Overlay Title'
}
To execute an overlay operation on continuation, add an object defining:
{
'type': 'overlay',
'action': 'actionname',
'target': 'http://tld.com',
'title': 'Overlay Title',
'css': 'someclass',
'close': false,
'uid': '1234'
}
Setting close to true, closes the overlay with uid. The UID gets passed
as ajax.overlay-uid request parameter to ajaxaction endpoint when
executing an overlay operation on client side.
An additional continuation feature is to display messages. To display a message, add an object defining:
{
'type': 'message',
'payload': 'Text or <strong>Markup</strong>',
'flavor': 'error',
'selector': null,
}
Either flavor or selector must be given. Flavor causes the message to
be shown in an overlay and could be one of ‘message’, ‘info’, ‘warning’ or
‘error’. If selector is given, the message gets displayed as content of the
DOM element identified by this selector. If both flavor and selector is set,
selector is ignored.
Note - Be aware that you can provoke infinite loops with continuation action and event operations, use this features with care.
Forms#
Ajax form processing is done by posting the form to a hidden iframe on the client. Treibstoff not expects a form processing endpoint with a specific name but in a defined response format:
<!--
This is the rendered form payload container. If it's desired
to stick to the form after sucessful form processing or if a
validation error occurs, the content of the container is
taken to rerender the form on the client.
-->
<div id="ajaxform">
<form ajax:form="true"
id="someform"
method="post"
action="http://example.com/myformaction"
enctype="multipart/form-data">
</form>
</div>
<!--
This script block reads the form payload from the container
and passes it among other options to the Ajax singleton.
Note that this code is executed inside the hidden iframe,
so the Ajax singleton needs to be accessed via ``parent``.
-->
<script language="javascript" type="text/javascript">
// Get response payload container
var container = document.getElementById('ajaxform');
// Extract form DOM element from payload container
var child = container.firstChild;
while(child != null && child.nodeType == 3) {
child = child.nextSibling;
}
/**
* Call ``ts.ajax.form`` on parent frame. It expects the
* form DOM element, the selector, the DOM manipulation
* mode, optional continuation operation definitions and
* a flag whether an error occured while processing the
* form. The error flag not means a validation error but
* an exception happened and is needed for proper application
* state handling.
*/
parent.ts.ajax.form({
payload: child,
selector: '#someform',
mode: 'replace',
next: [{}],
error: false
});
</script>
Attribute Reference#
This section contains a detailed description about all available Ajax operation related DOM attributes.
Operation#
ajax:bind="click other"Bind an Ajax operation on DOM element. Value contains a space separated list of events which triggers the operation. Events can be DOM events or any arbitrary custom event.
ajax:target="http://tld.com?param=value"Ajax target definition. Value consists of server target URL and an optional query string.
ajax:confirm="Do you really want to do this?"Show confirmation dialog whether to execute the operation. Value contains the confirmation message.
Action#
ajax:action="name1:selector1:mode1 name2:selector2:mode2"Perform one or more action operations. Action definitions are colon separated. Each action definition consists of a triple containing action
name,selectorandmode. Selector is a CSS selector defining the DOM element(s) which gets affected by the action. Mode defines the DOM manipulation mode which can be eitherinnerorreplace.
Event#
ajax:event="event1:selector1 event2:selector2"Trigger one or more event operations. Event definitions are colon separated. Each event definition consists of a tuple containing event
nameandselector. Name is the event name and can be any arbitrary custom event. Selector is a CSS selector defining the DOM element(s) to receive the event.
Overlay#
ajax:overlay="actionname"Renders an action operation to an overlay. Value contains the action
name. Other thanajax:action, value contains only the action name, selector mode are implicit.
ajax:overlay-uid="1234"Uid for the overlay. If not given, a UUID 4 gets generated as overlay uid.
ajax:overlay-title="Overlay Title"Renders title in overlay header.
ajax:overlay-css="someclass"Add an additional CSS class to Overlay DOM element.
Path#
ajax:path="/some/path"Sets the address bar path and pushes a state object containing operation definitions to session history stack if supported by browser.
If value is
href, path gets taken fromhrefattribute. If value istarget, path gets taken from eventajaxtargetproperty orajax:targetattribute. Otherwise value is taken as is.On window
popstateevent, the operations defined by the state object are executed. Possible operations are action, event and overlay.If no ajax operation is defined on state, a redirect to target is executed.
If state object not contains
_t_ajaxproperty, it gets ignored. This property is set transparently and ensures that only state objects are considered which has been added by path operations.popstate=1is added to requests made by path operations. This is useful to determine on server side whether to skip path operation in continuation operations.
ajax:path-target="http://tld.com?param=value"Operation target gets taken from
ajax:path-targetif set, otherwise falls back to target from eventajaxtargetorajax:target. Ifajax:path-targetcontains an empty value, target gets taken fromajax:path.
ajax:path-action="name:selector:mode"Action operation definition gets taken from
ajax:path-actionif set, otherwise falls back toajax:action. If value is empty, action execution is suppressed even ifajax:actionis set.
ajax:path-event="evt1:sel1"Event operation definition gets taken from
ajax:path-eventif set, otherwise falls back toajax:event. If value is empty, event dispatching is suppressed even ifajax:eventis set.
ajax:path-overlay="actionname"Overlay operation definition gets taken from
ajax:path-overlayif set, otherwise falls back toajax:overlay. If value is empty, overlay execution is suppressed even ifajax:overlayis set.
ajax:path-overlay-css="somclass"Additional CSS class for overlay gets taken from
ajax:path-overlay-cssif set, otherwise falls back toajax:overlay-css.
ajax:path-overlay-uid="1234"Overlay uid gets taken from
ajax:path-overlay-uidif set, otherwise falls back toajax:overlay-uid.
ajax:path-overlay-title="Overlay Title"Overlay title gets taken from
ajax:path-overlay-titleif set, otherwise falls back toajax:overlay-title.
Form#
ajax:formValid on form DOM elements. If set, form gets handles as ajax form and is posted to hidden iframe.
API#
The public API is available via the ajax singleton.
- class Ajax()#
Ajax singleton.
- Ajax.register(func, instant)#
Register binder callback function.
Integration of custom JavaScript to the binding mechanism is done via this function. The register function takes a callback function and a boolean flag whether to immediately execute the callback as arguments.
The passed binder callback gets called every time when markup is changed by this object and gets passed the changed DOM part as
context:$(function() { ts.ajax.register(function(context) { $('.sel', context).on('click', function() {}); }, true); });- Arguments
func (
function()) – Binder callback.instant (
boolean()) – Flag whether to execute binder callback immediately at registration time.
- Ajax.bind(context)#
Bind Ajax operations.
Parses given piece of DOM for Ajax operation related attributes and binds the Ajax dispatcher.
Additionally calls all registered binder functions for given context.
- Arguments
context (
$()) – jQuery wrapped piece of DOM.
- Returns
$ – The given jQuery wrapped context.
- Ajax.request(opts)#
Perform XMLHttpRequest request.
By default it sends requests of type
htmlwith methodgetand displays ats.ajax.errormessage if request fails:ts.ajax.request({ url: 'https://tld.com/some/path', params: { a: 'a', b: 'b' }, type: 'json', method: 'POST', cache: true, success: function(data, status, request) { }, error: function(request, status, error) { } });
Given
urlmight contain a query string. It gets parsed and written to request parameters. If same request parameter is defined in URL query and params object, latter one takes precedence.Success and error callback functions gets wrapped to handle ajax spinner automatically.
- Arguments
opts (
Object()) – Request options.opts.url (
string()) – Request URL.opts.params (
Object()) – Optional query parameters for request.opts.type (
string()) – Optional request type. Defaults to ‘html’.opts.method (
string()) – Optional request method. Defaults to ‘GET’.opts.cache (
boolean()) – Optional flag whether to cache the response. Defaults to False.opts.success (
function()) – Callback if request is successful.opts.error (
function()) – Optional callback if request fails. Default callback displays error message with response status.
- Ajax.action(opts)#
Execute action operation.
Requests
ajaxactionon server and modifies DOM with response according to mode and selector:let target = ts.ajax.parse_target('http://tld.com/some/path?param=value'); ts.ajax.action({ name: 'content', selector: '#content', mode: 'inner', url: target.url, params: target.params });
- Arguments
opts (
Object()) – Ajax options.opts.name (
string()) – Action name.opts.selector (
string()) – CSS selector of DOM element to modify with response payload.opts.mode (
string()) – Mode for manipulation. Eitherinnerorreplace.opts.url (
string()) – URL on whichajaxactiongets requested.opts.params (
Object()) – Query parameters.
- Ajax.trigger(opts)#
Execute event operation.
Creates an event providing
ajaxtargetandajaxdataproperties and trigger it on DOM elements by selector.The
ajaxtargetproperty on the event instance is an object containingurlandparamsproperties, as returned byAjax.parse_target:let url = 'http://tls.com?param=value'; ts.ajax.trigger({ name: 'contextchanged', selector: '.contextsensitiv', target: ts.ajax.parse_target(url); });
If given target is a URL string, it gets automatically parsed by the trigger function:
ts.ajax.trigger({ name: 'contextchanged', selector: '.contextsensitiv', target: 'http://tls.com?param=value' });
Optionally a
dataoption can be passed, which gets set at theajaxdataattribute of the event:ts.ajax.trigger({ name: 'contextchanged', selector: '.contextsensitiv', target: 'http://tld.com?param=value', data: {key: 'val'} });
Note - For B/C reasons,
triggercan be called with positional arguments (name, selector, target, data). This behavior is deprecated and will be removed in future versions.- Arguments
opts (
Object()) – Event options.opts.name (
string()) – Event name.opts.selector (
string()) – CSS selector of DOM elements on which to trigger events on.opts.target (
string|Object()) – Event target. Gets set asajaxtargetproperty on event instance.opts.data (
*()) – Optional event data. Gets set asajaxdataproperty on event instance.
- Ajax.path(opts)#
Execute path operation.
Wites browser session history stack. Executes Ajax operations on window popstate event.
Add an entry to the browser history:
ts.ajax.path({ path: '/some/path', target: 'http://tld.com/some/path', action: 'layout:#layout:replace', event: 'contextchanged:#layout', overlay: 'actionname', overlay_css: 'additional-overlay-css-class' });
If
replaceoption is given, browser history gets reset:ts.ajax.path({ path: '/some/path', target: 'http://example.com/some/path', action: 'layout:#layout:replace', replace: true });
- Arguments
opts (
Object()) – Path options.opts.path (
string()) – The path to write to the address bar.opts.target (
string()) – Related target URL.opts.action (
string()) – Ajax action to perform.opts.event (
string()) – Ajax event to trigger.opts.overlay (
string()) – Ajax overlay to display.opts.overlay_css (
string()) – CSS class to add to ajax overlay.opts.replace (
boolean()) – Flag whether to reset browser history.
- Ajax.overlay(opts)#
Execute overlay operation.
Load action result into an overlay.
Display action operation in overlay. Contents of the
titleoption gets displayed in the overlay header:ts.ajax.overlay({ action: 'actionname', url: 'https://tld.com', params: {param: 'value'}, title: 'Overlay Title' });
Optional to
urlandparams,targetcan be passed as option. If bothtargetandurl/paramsgiven,targettakes precedence:ts.ajax.overlay({ action: 'actionname', target: 'https://tld.com?param=value' });
If
cssoption is given, it gets set on overlay DOM element. This way it’s possible to add custom styles for a specific overlay:ts.ajax.overlay({ action: 'actionname', target: 'https://tld.com?param=value', css: 'some-class' });
Overlays get a generated UID by default for later reference which gets passed as
ajax:overlay-uidrequest parameter to the server.Ajax.overlayreturns the overlay instance, from which this uid can be read:let overlay = ts.ajax.overlay({ action: 'actionname', target: 'https://tld.com?param=value' }); let uid = overlay.uid;
Already open ajax overlays can be closed by passing the
closeoption and the overlayuid:ts.ajax.overlay({ close: true, uid: uid });
A callback can be provided when overlay gets closed by passing it as
on_closeoption:ts.ajax.overlay({ action: 'actionname', target: 'http://foobar.org?param=value', on_close: function(inst) { // inst is the overlay instance. } });
- Arguments
opts (
Object()) – Overlay options.opts.action (
string()) – Ajax action name.opts.url (
string()) – URL on whichajaxactiongets requested.opts.params (
Object()) – Query parameters.opts.target (
string|Object()) – Optional action target. Takes precedence overurlandparams.opts.title (
string()) – Title to display in overlay header.opts.css (
string()) – CSS class to add to overlay DOM element.opts.uid (
string()) – The overlay UID.opts.close (
boolean()) – Flag whether to close an open overlay.
- Returns
Overlay – Overlay instance.
- Ajax.form(opts)#
Render ajax form after form processing.
Gets called from hidden form iframe when response returns. See server integration documentation for details.
- Arguments
opts (
Object()) – Form options.opts.payload (
DOMElement()) – The rendered form.opts.selector (
string()) – CSS selector of the form.opts.mode (
string()) – DOM manipulation mode.opts.next (
Array()) – Continuation operation definitions.opts.error (
boolean()) – A flag whether an error occured while processing the form. The error flag not means a validation error but an exception happened and is needed for proper application state handling.
- class AjaxUtil()#
Ajax utility mixin.
- AjaxUtil.parse_target(target)#
Parse URL, query and path from URL string:
>> ts.ajax.parse_target('http://tld.com/some/path?param=value'); -> { url: 'http://tld.com/some/path', params: { param: 'value' }, path: '/some/path', query: '?param=value' }
- Arguments
target (
string()) – URL string to parse.
- Returns
Object – Containing
url,params,pathandquery.
- class AjaxSpinner()#
Ajax spinner.
The Ajax spinner is a singleton and is accessible via the
ajaxobject. Internally it holds a request count which gets increased every time theshowfunction is called. With eachhidecall, the request count gets decreased, and as soon as it reaches 0 the spinner disappears. This way it’s possible to perform simultaneous requests while avoiding flickering of the animation or the spinner disappearing while a request is still in progress.Show the spinner:
ts.ajax.spinner.show();
Hide the spinner. Spinner not disappears before request count reaches 0:
ts.ajax.spinner.hide();
Force closing of the spinner and reset request count:
ajax.spinner.close(true);
- AjaxSpinner.show()#
Show spinner animation.
- AjaxSpinner.hide(force)#
Hide spinner animation.
- Arguments
force (
boolean()) – Forces spinner to disappear and resets request count.
Deprecated functions#
- Ajax.parseurl(url)#
This function is deprecated. Use
ts.parse_urlinstead.
- Ajax.parsequery(url, as_string)#
This function is deprecated. Use
ts.parse_queryinstead.
- Ajax.parsepath(url, include_query)#
This function is deprecated. Use
ts.parse_pathinstead.
- Ajax.parsetarget(target)#
This function is deprecated. Use
ts.ajax.parse_targetinstead.
- Ajax.message(message, flavor)#
This function is deprecated. Use
ts.show_messageinstead.
- Ajax.info(message)#
This function is deprecated. Use
ts.show_infoinstead.
- Ajax.warning(message)#
This function is deprecated. Use
ts.show_warninginstead.
- Ajax.error(message)#
This function is deprecated. Use
ts.show_errorinstead.
- Ajax.dialog(opts, callback)#
This function is deprecated. Use
ts.show_dialoginstead.