Form Runner offline embedding API
Last updated
Last updated
[SINCE Orbeon Forms 2021.1]
As of Orbeon Forms 2023.1 this feature is considered in "beta" status.
See the feature for a rationale and for the quickest way to preview this feature.
As of Orbeon Forms 2023.1:
The Form Runner offline embedding API is mainly relevant for users who want to embed Orbeon Forms in another application, typically a web view within a mobile app.
Orbeon Forms does not yet provide an offline mode for end users of the regular Form Runner web application. For more about this, see .
An Orbeon form first needs to be compiled into a serialized representation. This is done on the server-side, and the result is a zip file containing the compiled form definition, as well as any resources referenced by the form.
In order to obtain a compiled form definition, you call the following service endpoint with an HTTP GET
request:
The resulting binary data can later be passed to the client-side renderForm()
API.
The API entry point is from the global JavaScript object:
You must provide a specific configuration before running renderForm()
and other methods:
The first argument is a SubmissionProvider
instance, which is described below. This tells Form Runner how to read and write form data, as well as how to call services.
The second, optional parameter, is the size of the compiled form cache, in number of compiled forms. This is used to limit the number of compiled forms kept in memory.
The effect of calling configure()
is global. It applies to all subsequent calls to renderForm()
.
You can render the form in a given HTML element with id orbeon-wrapper
as follows:
This returns a Promise
which resolves when the form initialization is complete.
The compiledFormDefinition
parameter is the binary data obtained from the server-side compilation API, as a Uint8Array
.
The third argument is a JavaScript object with the following properties:
appName
string
formName
string
formVersion
number
positive integer
mode
string
new
|edit
|view
documentId
string?
for edit
|view
modes only
queryString
string?
optional
headers
Headers?
optional
formData
string?
for POST
ed form data
If formData
is defined, it must be a string containing form data in XML format. This is the equivalent of performing an HTTP POST
when online.
Note that regular reading/writing data is done through the SubmissionProvider
interface, which is described below.
If you are only loading one form in the page, you can chain the calls as follows:
When running in offline mode, Form Runner needs to be able to:
read form data
write form data
call services
This is done through the SubmissionProvider
interface, which is implemented by the embedder:
SubmissionProvider
This is the main entry point. You implement your own SubmissionProvider
class and pass it to the ORBEON.fr.FormRunnerOffline.configure()
function.
This class has two methods:
submit()
synchronous call
only method supported until Orbeon Forms 2023.1
deprecated as of Orbeon Forms 2023.1
submitAsync()
asynchronous call
used for saving data
used for asynchronous service calls
SubmissionRequest
This is the request object passed to the submit()
and submitAsync()
methods.
It has the following properties:
method
possible HTTP method
GET
, POST
, PUT
, DELETE
, HEAD
url
JavaScript URL
object
this is the URL of the resource to be accessed
headers
JavaScript Headers
object
HTTP headers associated with the request, including:
Content-Type
: for methods that have a body: POST
, PUT
Orbeon-Workflow-Stage
: workflow stage information
Orbeon-Form-Definition-Version
: form definition version information
body
optional for methods that don't have a body: GET
, HEAD
, DELETE
, in which case it can be left undefined
required for methods that have a body: POST
, PUT
Uint8Array
or ReadableStream<Uint8Array>
Uint8Array
is used for synchronous calls
ReadableStream<Uint8Array>
is used for asynchronous calls
Using ReadableStream
is the most difficult part of this API. This standard JavaScript API exposes a way to get data in a streamable way, in chunks, asynchronously. This means that you don't need to have all your data in memory at once, and you can start processing data as soon as it is available. You can also move data across network or application boundaries asynchronously, for example between a Web View and a native app.
SubmissionResponse
This is the response object returned by the submit()
and submitAsync()
methods.
It has the following properties:
statusCode
HTTP status code
200
, 201
, 204
, 400
, 401
, 403
, 404
, 500
, etc.
headers
JavaScript Headers
object
HTTP headers associated with the response, including:
Content-Type
: for methods that return a body: POST
, GET
Orbeon-Workflow-Stage
: workflow stage information when reading form data
body
for methods that return a body: POST
, GET
, left undefined
for methods that don't return a body
Uint8Array
or ReadableStream<Uint8Array>
Uint8Array
is used for synchronous calls or asynchronous calls that return a body synchronously
ReadableStream<Uint8Array>
is used for asynchronous calls that return a body asynchronously
Form Runner calls are made through the SubmissionProvider
interface.
Form Runner persistence calls (saving and reading form data and attachments) use submitAsync()
instead of submit()
in call cases.
Form Runner starts by saving attachments, if needed (see below). Only if saving all attachments succeeds does Form Runner save the form data itself.
Then Form Runner saves the form data itself.
submitAsync()
is called
the method
is PUT
the url
is the URL of the form data resource
for save-final
: /fr/service/persistence/crud/$app/$form/data/$document/data.xml
for save-progress
: /fr/service/persistence/crud/$app/$form/draft/$document/data.xml
the headers
include:
Content-Type
: application/xml
Orbeon-Workflow-Stage
: workflow stage information
Orbeon-Form-Definition-Version
: form definition version information
The XML body is passed as a ReadableStream<Uint8Array>
. You can convert that to a Promise<Uint8Array>
if needed.
When Form Runner needs to read form data, it calls:
submitAsync()
the method
is GET
the url
is the URL of the form data resource
for reading final data: /fr/service/persistence/crud/$app/$form/data/$document/data.xml
for reading autosaved data: /fr/service/persistence/crud/$app/$form/draft/$document/data.xml
the headers
must include:
Content-Type
: application/xml
Orbeon-Workflow-Stage
: workflow stage information, if applicable (in particular if this was specified when the data was saved)
Orbeon-Form-Definition-Version
: form definition version
Here, the XML body can be returned as a ReadableStream<Uint8Array>
or directly as a Uint8Array
.
This works like saving data, except that:
the url
is the URL of the attachment resource
for save-final
: /fr/service/persistence/crud/$app/$form/data/$document/$attachment.bin
for save-progress
: /fr/service/persistence/crud/$app/$form/draft/$document/$attachment.bin
the headers
include:
Content-Type
: application/octet-stream
Orbeon-Form-Definition-Version
: form definition version
Currently, Form Runner always uses a .bin
extension for attachments, even if the original file had a different extension. Similarly, Form Runner always uses application/octet-stream
as the Content-Type
for attachments, even if the original file had a different Content-Type
.
Form Runner will issue one call to submitAsync()
per attachment.
Again, an attachment body is passed as a ReadableStream<Uint8Array>
. The main difference, compared with form data, is that attachments can be typically much larger: from a few megabytes to gigabytes, when images and videos are attached, for example. This makes it all the more important to leverage ReadableStream
to avoid having to load the entire attachment in memory at once.
TODO
Form Runner will issue service calls through the SubmissionProvider
API as well. This includes form author-defined service calls in Form Builder, as well as services calls specified in the Dynamic Dropdown, in particular.
For example a Dynamic Dropdown might call a service at the following URL with the GET
method
Make sure you return a Content-Type
header with a value of application/xml
or application/json
, depending on the format you return.
The service can then retrieve the list of countries from a database, and return the list as XML or JSON in a response body. Here again, you can return either a ReadableStream<Uint8Array>
or a Uint8Array
.
see the documentation
see the documentation
see also the JavaScript documentation
of a demo SubmissionProvider implementation which uses ReadableStream
. The example is written in Scala, but the same exact logic applies to a JavaScript or TypeScript implementation.
see the documentation
see the documentation
this is only called if is enabled
this is only called if is enabled