public
Last active

XForms action in XML syntax and with tentative CoffeeScript syntax

  • Download Gist
CoffeeScript with jQuery.coffee
CoffeeScript
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
instance = model.instances['fr-persistence-instance']
resources = x.models['fr-resources-model'].vars['$fr-fr-resources']
 
$('error', instance).replaceWith event.response-body
$('is-error', instance).replaceWith 'true'
 
x.message 'Error with submission: ' + event.submission-id, 'xxf:log-debug'
x.message event.response-body, 'xxf:log-debug'
 
switch event['error-type']
when 'validation-error'
$('message', instance).replaceWith $('detail messages form-validation-error', resources)
$('#fr-message-validation-error').toggle()
model.dispatch 'fr-visit-alerts'
x.models['fr-sections-model'].dispatch 'fr-expand-all'
when 'xxforms-pending-uploads'
x.message resources.detail.messages['upload-in-progress']
else
$('message', instance).replaceWith $('detail messages database-error', resources)
$('#fr-message-fatal-error').toggle()
CoffeeScript.coffee
CoffeeScript
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
instance = model.instances['fr-persistence-instance']
resources = x.models['fr-resources-model'].vars['$fr-fr-resources']
 
instance.error = event.response-body
instance['is-error'] = 'true'
 
x.message 'Error with submission: ' + event.submission-id, 'xxf:log-debug'
x.message event.response-body, 'xxf:log-debug'
 
switch event['error-type']
when 'validation-error'
instance.message = resources.detail.messages.form-validation-error
x.toggle 'fr-message-validation-error'
model.dispatch 'fr-visit-alerts'
x.models['fr-sections-model'].dispatch 'fr-expand-all'
when 'xxforms-pending-uploads'
x.message resources.detail.messages['upload-in-progress']
else
instance.message = resources.detail.messages.database-error
x.toggle 'fr-message-fatal-error'
Scala example.scala
Scala
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
val instance = model.instances("fr-persistence-instance")
val resources = models("fr-resources-model").vars("$fr-fr-resources")
 
instance \ "error" setvalue event.responseBody
instance \ "is-error" setvalue "true"
 
message("Error with submission: " + event.submissionId, "xxf:log-debug")
message(event("response-body"), "xxf:log-debug")
 
event("error-type") match {
case "validation-error" =>
instance \ "message" setvalue (resources \ "detail" \ "messages" \ "form-validation-error")
toggle("fr-message-validation-error")
model.dispatch("fr-visit-alerts")
models("fr-sections-model").dispatch("fr-expand-all")
case "xxforms-pending-uploads" =>
message(resources \ "detail" \ "messages" \ "upload-in-progress")
case _ =>
instance \ "message" setvalue (resources \ "detail" \ "messages" \ "database-error")
toggle("fr-message-fatal-error")
}
XForms actions.xml
XML
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
<xf:action ev:event="fr-submit-error">
<!-- Remember there was an error -->
<xf:setvalue ref="instance('fr-persistence-instance')/error" value="event('response-body')"/>
<xf:setvalue ref="instance('fr-persistence-instance')/is-error">true</xf:setvalue>
<!-- Log error -->
<xf:message level="xxf:log-debug">Error with submission: <xf:output value="event('submission-id')"/></xf:message>
<xf:message level="xxf:log-debug"><xf:output value="event('response-body')"/></xf:message>
 
<!-- case validation-error => set error message and show validation error section in UI -->
<xf:action if="event('error-type') = 'validation-error'">
<xf:setvalue ref="instance('fr-persistence-instance')/message" value="$fr-fr-resources/detail/messages/form-validation-error"/>
<xf:toggle case="fr-message-validation-error"/>
<!-- Mark all active alerts as visited -->
<xf:dispatch name="fr-visit-alerts" target="fr-persistence-model"/>
<!-- Open all sections -->
<xf:dispatch name="fr-expand-all" target="fr-sections-model"/>
</xf:action>
 
<!-- case xxforms-pending-uploads => display a modal message -->
<xf:action if="event('error-type') = 'xxforms-pending-uploads'">
<xf:message model="fr-resources-model" value="$fr-fr-resources/detail/messages/upload-in-progress"/>
</xf:action>
 
<!-- case _ => set error message and show fatal error section in UI -->
<xf:action if="not(event('error-type') = ('validation-error', 'xxforms-pending-uploads'))">
<xf:setvalue ref="instance('fr-persistence-instance')/message" value="$fr-fr-resources/detail/messages/database-error"/>
<xf:toggle case="fr-message-fatal-error"/>
</xf:action>
</xf:action>
XQuery example.xq
XQuery
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
let $instance := xf:instance('fr-persistence-instance')
let $resources := xf:model('fr-resources-model', 'fr-fr-resources')
let $messages := $resources/detail/messages
return
(
replace value of node $instance/error with xf:event('response-body'),
replace value of node $instance/is-error with 'true',
 
xf:message(
('Error with submission: ', xf:event('submission-id'), 'xxf:log-debug')
'xxf:log-debug'),
xf:message(xf:event('response-body'), 'xxf:log-debug'),
 
switch ( xf:event('error-type') )
case 'validation-error' return (
replace value of node $instance/message with $messages/form-validation-error,
xf:toggle('fr-message-validation-error'),
xf:dispatch('fr-visit-alerts', xf:model('fr-persistence-model')),
xf:dispatch('fr-expand-all', xf:model('fr-sections-model'))
)
case 'xxforms-pending-uploads' return (
xf:message($messages/upload-in-progress)
)
default return (
replace value of node $instance/message with $messages/form-validation-error,
xf:toggle('fr-message-fatal-error')
)
)

The idea here is to explore how one can rewrite complex actions written with the XForms action syntax into a more lightweight notation, here CoffeeScript.

The original example is taken from an actual action in Form Runner. It is just one example of a non-trivial action (and there are way more complex ones in Form Runner!).

The API is very tentative. Ideas include:

  • expose an object-oriented JavaScript API
  • provide native XML navigation into XML data, for example through E4X (JavaScript) or at least simple "dot" notation

The runat attribute is useful only for server-side implementations, to differentiate between scripts that must run on the client.

Comments welcome.

Being more jQuery-like, for things that have ids (controls, instances, models…), you could imagine, instead of:

x.toggle "fr-message-validation-error"

To write:

$("#fr-message-validation-error").toggle()

That that would be an option. I see two distinct uses:

  • XForms objects such as controls, models, binds (jQuery-like, as we might not actually have a DOM to work on)
  • instance data (actual jQuery)

For access to instance data, there are several options:

  • XPath API
  • jQuery
  • E4X or E4X-like
  • DOM (don't need to use this directly, except if it's as basis for jQuery)

I added an attempt at using jQuery syntax for both accessing controls and data.

The following:

$('error', instance).replaceWith event.response-body

Feels like it perverts the jQuery interface. In jQuery, if something has an id, you'd expect to be able to use $("#id"), not $("id", collection). Also, maybe it is just fine to say xforms.instances["invoice"], which looks similar to document.forms["some-form"]. In some cases, this will allow one to write xforms.instances.invoice, which is syntactically lighter.

Mmh, $('error', instance) means selecting an element called error in the document instance. It's the same as instance('instance')//error.

Ah, yes, in that case I agree. I incorrectly assumed $('error', instance) meant instance('error') instead of instance('instance')//error.

Shouldn't this:

model.dispatch 'fr-visit-alerts'

actually be this instead?:

x.models['fr-persistence-model'].dispatch 'fr-visit-alerts'

In response to http://twitter.com/ebruchez/status/30046769760964608 . I thought more about integrating XForms within XQuery or the other way around, instead of replicating the XForms features in another language. But I guess something like the following would do (you know, more or less):

See file

I think model.dispatch 'fr-visit-alerts' is right: model is implicitly pointing to the fr-persistence-model model. The idea was to have some JS objects automatically scoped for convenience, and model would tentatively one of them.

@fgeorges Thanks for the XQuery example. It looks similar enough to the JS and CS examples. I put it up as a file a the top (not sure how I can allow others to edit/add files?).

@ebruchez You're saying that model could be automatically bound to an object representing the current model?

@avernet Yes, as if the enclosing xf:action passed it as a function parameter.

Please sign in to comment on this gist.

Something went wrong with that request. Please try again.