- 1 Spec text
- 2 Reasoning
- 3 Events Relating to Transformations
- 4 Use cases
It should be possible to execute an XSLT transformation on data. Currently we are discussing the pro's and con's of using a transform function or an action.
There are currently two proposed solutions. The first solutions uses an action to execute the transform and store the result in an instance. The second solution is a function that returns the result of the transform. On 2010-11-01 (TPAC) we decided to go for the function approach.
To let the function approach work you should:
- Mark all the ancestors as changed if a node is changed (works around the recalculate issue)
- Make the result tree read-only
- The result is a node if the XSLT output method is XML, a String if the XSLT output method is text or html
Function approach (currently in favor)
- Can be used in all actions, in a ref on a submission, and value attribute
- Automatic update in the UI when data changes (xf:output)
- Can be easily chained in functions and used in all actions
- Doesn't requires an extra instance to store the intermediate result
- More 'declarative' and author friendly in some use cases (see two use cases below)
- Function can't be used everywhere were XPath functions are allowed. Function doesn't has a meaning on a ref and nodeset of a bind, shouldn't be allowed on a ref and nodeset in the UI layer or MIP attributes of a bind (could be allowed in the future when we have more implementation experience).
- Over ruled : the resulting node (and its descendants will be read-only)
- Can refer external resources and therefore we don't easily know when they change (we could make the URI resolution stable, i.e. calling the same function twice within the same 'instance of the form' with the same arguments returns the same result)
- Requires dependency on complete subtree (can automatically works when you use DOM mutation events) (we also need this for id())
- Over ruled: Mark all the parents as changed if a node is changed -> when a descendant of the input node changes, the function will re-run automatically
- No facility to handle errors generated by the transformation.
- Without XPath 2.0, and therefore support for sequences, the declaration of parameters is problematic. It would requeire an extra instance (verbose, extra memory, extra markup)
- Object xf:transform(String, Node?)
The result is a node the resulting node of the XSLT transformation if the XSL output method is XML, the resulting String otherwise. The first argument is the URI of the the XSLT stylesheet. The optional second argument is the node that should be used as input for the XSLT transformation, when the second parameter is omitted the current context node will be used as input.
Action approach (for now we recommend implementing the transform function)
- Can be used along side any action.
- Only executes when triggered by an event (no need for dependencies).
- By the introduction of an xforms-transform-done event, and the use of conditional event execution, it is possible to provide flexibility in how updates are handled where the output of a transform is 'piped' into the UI.
- Using mark-up to declare the transform provides greater flexibility and clarity in defining transformation options/properties including whether the transform is dereferenced via a URI or from a instance via XPath.
- By following the submission headers example, the transform action can declare parameters in-line either statically or reference values in an instance data.
- If an error event for transforms were introduced xforms-transform-error then this could be used to trap errors in the same way as xforms-submit-error handles submission errors.
- Can re-uses common attribute and mark-up patterns from related actions/features like insert and submission, respectively, thus making it consistent with existing XForms practices.
- Requires an extra instances in a lot of use cases (verbose, extra memory, extra markup)
- No automatic update in the UI when data changes (xf:output)
- Can not be used in other actions, nor as parameter of a function without, nor in a ref on submission, nor on a value attribute
- Duplication of all attributes of insert on this action
Events Relating to Transformations
The three best known, and currently accepted, methods for transforming XML are XSLT, XProc and XQuery. Although they don't all share the same representation and neither do they have, for what of a better expression, a common invocation signature they do all share one important aspect - they can fail.
It has been proposed that the xforms-compute-exception event be dispatched when a transformation fails. This may be sufficient for the transform function, seeing as it is evaluated in an XPath expression but would it be equally applicable in the context of an XForms action?
With that in mind, should an xforms-transform-error and it's companion, xforms-transform-done events be introduced to handle both the error condition and the successful completion respectively? The reason I include xforms-transform-done is because the form author may wish to trigger some further action once the transformation is complete. Dare I say it, their may be scope for an xforms-transform event that heralds the start of a transform. With such a set of events one could, if one chose, build strong and complex dependency chains.
Adapt data that is submitted
Transform the data that is in the form before submitting it, to align the data with what the submission end point expects.
<xforms:submission id="s1" ref="transform('my-stylesheet.xsl', instance())" resource="..."/> ... <xforms:submit submission="s1"> <xforms:label>submit</xforms:label> </xforms:submit>
<xforms:submission id="s1" ref="instance('transformed')" resource="..."/> <xforms:instance id="transformed"> <dummy/> </xforms:instance> <xforms:action ev:event="submit-data"> <xforms:transform src="my-stylesheet.xsl" root="instance()"/> <xforms:dispatch name="xforms-submit" target="s1"/> </xforms:action> ... <xforms:trigger> <xforms:label>submit</xforms:label> <xforms:dispatch name="submit-data" target="model"/> </xforms:trigger>
Or alternatively, and more concisely, the transform can be triggered directly from the submission:
<xforms:submission id="s1" ref="instance('transformed')" resource="..."> <action ev:event="xforms-submit"> <xforms:transform src="my-stylesheet.xsl" root="instance()"/> </action> </xforms:submission>
Transform data before displaying
Display the content in a more attractive way. (Note: it would be 'preferable' that output also accept complex content then we could omit the serialize.
<xforms:output value="serialize(transform('my-stylesheet.xsl', instance()))" mediatype="application/xhtml+xml"/>
<xforms:instance id="transform-result"> <result/> </xforms:instance> <xforms:output ref="instance('transform-result')" /> <xforms:listen-to-data-change …> <!-- A new element that listens to data changes of a subtree --> <xforms:transform src="my-stylesheet.xsl" root="instance()"/> </xforms:listen-to-data-change>
The xforms:listen-to-data-change instruction is probably not necessary seeing as an action that is triggered by a DOM Mutation Event such as DOMSubtreeModified would be do the job nicely.