The submission element is not an action handler, or a form control, but a collection of attributes that guide: how to obtain some data to be sent, whether to validate it first, how to transport the data, what to do with any data that is returned, and so on.
Submission should therefore be seen as a series of phases, with the various attributes controlling what happens at each step of the process:
xforms-submit event to indicate that sending data is about to begin;xforms-submit-done is dispatched;xforms-submit-error is dispatched.Note that if new data arrives and is placed in a target, a rebuild of the model that holds the instance will be necessary.
We'll look at all of these steps in more detail in the following sections.
The data to be submitted is selected by evaluating the expression in the ref attribute. If no attribute is present, then its value is "/", which in effect means the first instance in the containing model. If a value for @ref is present, it represents the root of a fragment which will be used to find the data to be submitted. This obviously means that it is not necessary to submit an entire instance--smaller parts can be sent.
Sometimes an author will not want to serialise any data for submission, and for this reason XForms 1.1 adds the serialize attribute which can be set to false to indicate no data should be sent. But assuming this attribute is set to true, or is not set at all, then starting at the root of the fragment, all relevant nodes--i.e., all nodes that have their relevant Model Item Property evaluating to true--are collected to make up the data to be sent.
A common pattern is for authors to use relevance only to control the way that the user interface appears to their users (since non-relevant controls are hidden), but to still want all of the data in the instance to be submitted. To make this possible, XForms 1.1 introduced the relevant attribute, which can be set to false to indicate that even non-relevant nodes should be serialised for submission.
Note that the value in @ref also sets the evaluation context for any XPath expressions that might occur within the submission element.
The submission 'method' indicates the operation to be performed. Although this will often be a simple mapping to an HTTP method, it needn't be. For example, the following code sets up a submission declaration that when called will push data up to a server, using an HTTP PUT:
<xf:submission action="http://www.example.org/customer/3.xml" method="put" />
However, the following submission declaration differs only in that the file: protocol is used, yet it has a completely different effect--instead of sending data over the network using the HTTP protocol, the data is simply saved to a local file:
<xf:submission action="file:///my-files/customers/customer/3.xml" method="put" />
In other words the submission method is more than just an HTTP method.
(For more on the file: protocol, see Manipulating local files.)
The best way to understand the submission method therefore is as a request to the XForms processor to do something, but without having to be protocol-specific about what you want doing. This ability to specify what you want to do in a high-level way is very powerful. For example, say we would like to create a form that uses both of the URLs we had above--the customer on the server, and one on our local drive. The easiest way to achieve this would be for our form to use relative paths, and then if we run the form from our disk it saves the information to the local file, and if we run the form from the server it sends the data to the server. In terms of specifying the location it's easy; we just use relative paths as we might do for an image or stylesheet in HTML. But the method would normally be more of a problem, since the action to 'save a file' is completely different to the action to 'send data to a server using HTTP'. However, since the submission method is merely a request to the XForms processor to do something in a protocol-independent way, then the following mark-up will work in both of our scenarios--server and local drive alike:
<xf:submission action="customers/customer/3.xml" method="put" />
In short, the exact meaning of 'put' will be determined by the context in which it is being used, and it might even mean 'send an email':
<xf:submission action="mailto:accounts@example.org" method="put" />
We saw above that the submission method is usually specified using the method attribute; this is true in both XForms 1.0 and XForms 1.1, However, in XForms 1.1 the submission method can also be set using the method child element, which will override the method attribute if both are specified.
(Note that there is no default value for the submission method so either an attribute or element will be needed.)
The element technique allows us to set the submission method at run-time which is a particularly useful way of reusing one submission declaration for a number of different purposes, as we often want to do when building RESTful applications.
For example, in our earlier examples we had some customer information stored at:
customers/customer/3.xml
In a RESTful application we might allow this data to be updated with a 'put' or removed completely with a 'delete'. With XForms 1.0 we'd have to have two separate submission declarations:
<xf:submission id="sub-put" action="customers/customer/3.xml" method="put" /> <xf:submission id="sub-delete" action="customers/customer/3.xml" method="delete" />
However, in XForms 1.1 we can reuse one submission declaration for both tasks, as follows:
<xf:submission id="sub-customer" action="customers/customer/3.xml"> <xf:method value="method" /> </xf:submission>
Now that this submission declaration has been created, it can be used in any number of ways. For example, the submission method need not be set until the user chooses an action:
<xf:trigger>
<xf:label>Save</xf:label>
<xf:action ev:event="DOMActivate">
<xf:setvalue ref="method">put</xf:setvalue>
<xf:send submission="sub-customer" />
</xf:action>
</xf:trigger>
<xf:trigger>
<xf:label>Delete</xf:label>
<xf:action ev:event="DOMActivate">
<xf:setvalue ref="method">delete</xf:setvalue>
<xf:send submission="sub-customer" />
</xf:action>
</xf:trigger>
Alternatively, if we want to keep things more encapsulated, we might put this functionality into the submission declaration itself:
<xf:submission id="sub-customer" action="customers/customer/3.xml">
<xf:method value="method" />
<xf:action ev:event="my-save">
<xf:setvalue ref="method">put</xf:setvalue>
<xf:send submission="sub-customer" />
</xf:action>
<xf:action ev:event="my-delete">
<xf:setvalue ref="method">delete</xf:setvalue>
<xf:send submission="sub-customer" />
</xf:action>
</xf:submission>
This provides us with a convenient way of centralising any interaction with the customer into one place, which is particularly useful for keeping code up-to-date, or for managing processes that need to run when another has completed (or failed). To use this submission declaration the triggers we saw earlier would need to change to:
<xf:trigger>
<xf:label>Save</xf:label>
<xf:action ev:event="DOMActivate">
<xf:dispatch name="my-save" target="sub-customer" />
</xf:action>
</xf:trigger>
<xf:trigger>
<xf:label>Delete</xf:label>
<xf:action ev:event="DOMActivate">
<xf:dispatch name="my-delete" target="sub-customer" />
</xf:action>
</xf:trigger>
This makes the purpose of the code easier to see and read, and consequently easier to maintain.
After the selection step, the data to be submitted is then validated. No validation events are dispatched during this step, but the whole process of validation is the same as defined in xforms-revalidate. If any of the selected data has its required property set, but is found to be empty, or is invalid according to its constraint properties or a schema, then submission processing is stopped after dispatching event xforms-submit-error.
Since there will be situations where invalid data needs to be submitted--for example to save an incomplete form--XForms 1.1 introduced the validate attribute, which can be set to false to prevent this step of the processing. Validation is also disabled if @serialize is set to false, but since there may be situations where in invalid instance should halt submission, even if no data is being serialised, this can be overridden by setting @validate explicitly.
Although submission is a powerful feature of version 1.0 of XForms, there are a number of common use-cases that it is unable to support. For example, although it's easy for an XForms 1.0 form to request an RSS feed and to display the data with a repeat, it's not possible for the user to specify the feed location at run-time, perhaps by typing a URL into an input control, or selecting from a list. XForms 1.1 adds a number of new features to submission to control the URL, request headers, location of returned data, and more, which together make it much easier to build forms that use servers based on REST, ATOM, SOAP, and WebDAV.
In addition to the RSS feed example, other configurations that require URIs to be defined at run-time include REST-based services. For example, Basecamp incorporates the user's name into the URL, and as such it is impossible to create a generic form with XForms 1.0, even though XForms is ideal for communicating with such services.
Perhaps the most important example of a service that can't be used with XForms 1.0 is ATOM. Whilst at a push a form could be created to provide support for Basecamp (by creating one form per person!) there is simply no workaround that would allow ATOM to be used, since the URL that should be used for adding and editing items is provided within the item itself (a process called introspection).
Just as important as setting the URL at run-time is the need to specify request headers; both WebDAV and SOAP, for example, require header values to be set, as does HTTP authentication. (Whilst most XForms processors support HTTP authentication using the usual pop-up login box, by being able to set headers on a request it's possible for a form author to create their own login mechanism.)
A good example of the need for headers is when making a SOAP request. The action to be performed on the server is usually passed as a header in the HTTP request. Using the XForms 1.1 additions, this could be configured as follows:
<xf:submission
ref="instance('inst-request')"
method="post"
replace="none"
>
<xf:resource value="instance('inst-control')/submission/@action" />
<xf:header>
<xf:name>SOAPAction</xf:name>
<xf:value value="concat(
'"http://schemas.xmlsoap.org/wsdl/http/',
local-name(instance('inst-request')/soap:Body/*),
'"'
)"
/>
</xf:header>
</xf:submission>
One nice feature of this example is that the 'method' to use in the SOAPAction header is calculated for us automatically from the actual SOAP payload.
Whatever technique is used to get the URI into the submission, a little trick you can use to allow the user to choose a file is to use the xf:upload control to obtain a URL, as follows.
First, create a submission that uses a dynamic URL:
<xf:submission
ref="instance('inst-request')"
method="post"
replace="none"
>
<xf:resource value="instance('inst-control')/submission/@action" />
</xf:submission>
Next give the node that will hold the URL a datatype of 'xsd:anyURI', so that it can be used with the xf:upload control:
<xf:bind nodeset="instance('inst-control')/submission/@action" type="xsd:anyURI" />
Finally, use an xf:upload control to allow the user to provide a URL:
<xf:upload ref="instance('i-config')/submission/@action">
<xf:label>Choose file</xf:label>
</xf:upload>
The xf:upload control is often used to 'upload' the contents of a file into the instance data, but by binding the control to a node that has a datatype of xsd:anyURI we tell the control that all we want is the URL for the file, and not the file itself.
New features are due to be added to the XForms specification, to allow submission to handle incoming non-XML data. A new possible value for the replace attribute--text--prevents a submit-error occurring on receipt of non-XML data, whilst a new attribute--target--contains an XPath expression indicating the node into which response data is to inserted.
Here is an example of how to use the new attributes to insert the content of the file SomeTextFile.txt into the node /x/y in the instance data.
<xf:model>
<xf:instance id="i0">
<x>
<y />
</x>
</xf:instance>
<xf:submission
method="get" action="SomeTextFile.txt"
replace="text" target="y"
/>
</xf:model>
Although this has not been fully discussed and decided by the PTB, here is my interpretation of the changes to be made to Section 11: Submit of the XForms spec:
5.
Once the XML instance data has been replaced, the rebuild, recalculate, revalidate and refresh operations are performed on the model, without dispatching events to invoke those four operations. Submit processing then concludes after dispatching xforms-submit-done.
More interesting uses can be seen when submitting to a server that supports XPointer queries. Consider the situation where a single value is required from a large XML document; prior to the introduction of this feature, the following unwieldy code would be required:
<xf:model>
<xf:instance id="iRS">
<dummy />
</xf:instance>
<xf:instance id="i">
<x>
<y />
</x>
</xf:instance>
<xf:submission
method="get" action="AnEnormousXMLFile.xml"
instance="iRS" replace="instance"
>
<setvalue ev:event="xforms-submit-done"
ref="instance('i')/y"
value="instance('iRS')/some/deeply/nested/node"
/>
</submission>
</model>
With the target attribute, and @replace="text", it can be condensed into the following:
<xf:model>
<xf:instance>
<x>
<y />
</x>
</xf:instance>
<xf:submission
method="get" action="AnEnormousXMLFile.xml#xpointer(/some/deeply/nested/node/text())"
target="y" replace="text"
/>
</xf:model>
A spin-off from this feature is that the target attribute can also be used in conjunction with @replace="instance", and so build up complex documents by inserting retrieved sub-trees into the current instance, instead of replacing it entirely. This can be particularly handy when dealing with large XForms documents containing instance data with potentially many optional sections. In this way, it can work like relevance, but in reverse, adding the sections you need, rather than disabling those you don't.
For example:
<xf:model>
<xf:instance>
<x>
<y />
</x>
</xf:instance>
<xf:submission
method="get" action="ASmallXMLFile.xml"
target="y" replace="instance"
/>
</xf:model>
The above example would insert the XML content of the document at ASmallXMLFile.xml, into the y element.