Through the ages, whenever new technologies are introduced, as often as not they are quite unnecessarily made to look like the thing they are replacing, often to the detriment of users.
Forms also suffer under their legacy of paper.
Early web forms were often required to be identical to their paper versions, thus negating some of the advantages of electronic forms.
"You know it when you see it"
"Rectangular boxes you can type in"
Is a Dialogue box a form?
How about when you are asked to click on a location in a map?
Classically, a form drives the data:
The controls on the form dictate the structure of the underlying data being collected.
The data drives the form, and not vice versa:
The structure of the underlying data causes the controls to emerge.
The form is no longer static, but dynamically reflects the data.
Three examples:
Separation of data from controls.
Similar to separation of style from content with CSS, with similar advantages.
Instances of data + properties.
<instance src="sale.xml"/> <bind ref="..." property/>
Example property: Relevance
<bind ref="cc-number" relevant="../payment-method = 'credit'"/>
Controls in the UI then bind to data nodes, inheriting their properties.
<input ref="cc-number" label="Credit card number"/>
Here is an example (try it). If you select Credit card as payment type, the controls for that appear.
A classic form.
Written for an internet community.
The classical markup would look like this:
Please help us discover problems and solutions that would improve our processes. <select1 ref="choice"> <label>How can you help?</label> <item><label>You know a problem that needs to be fixed</label> <value>problem</value></item> <item><label>You know a 'solution' that doesn't work</label> <value>failure</value></item> <item><label>You have a prediction about a future possible failure</label> <value>prediction</value></item> </select1>
One a choice has been made, the questions for that choice are displayed:
<switch ref="choice"> <case name=""/> <!-- Until a choice is made, nothing is displayed --> <case name="problem"> <group ref="problem"> <label>You know a problem that needs to be fixed</label> <textarea ref="problem"><label>What problem do you see?</label></textarea> <textarea ref="solution"><label>Can you propose a solution?</label></textarea> </group> </case> <case name="failure"> ... etc ...
The data being produced would look like this:
<data> <choice>problem</choice> <problem> <problem>Too slow!</problem> <solution>Bigger pipes</solution> </problem> <failure> <problem/> <solution/> </failure> <prediction> <problem/> <likely/> <who/> <solution/> </prediction> </data>
Making it multilingual was easy. Instead of literal labels, they get their text from an instance:
<group ref="problem"> <label>You know a problem that needs to be fixed</label> <textarea ref="problem"><label>What problem do you see?</label></textarea> <textarea ref="solution"><label>Can you propose a solution?</label></textarea> </group>
⇒
<group ref="problem"> <label ref="instance('m')/problem/label"/> <textarea ref="problem"> <label ref="instance('m')/problem/problem"/></textarea> <textarea ref="solution"><label ref="instance('m')/problem/solution"/></textarea> </group>
Once we had made this change, we realised that the three cases were almost identical.
We could replace the whole switch with three options with a single group, essentially a single option that adpats to the data:
<switch ref="choice"> <case name=""/> <case name="problem">... problem, solution <case name="failure">... problem, solution <case name="prediction">... problem, likely, who, solution </switch>
⇒
<group ref="answer[@choice=../choice]"> <label ref="instance('m')/answer[@choice=context()/@choice]/label"/> <textarea ref="problem">...</textarea> <select1 ref="likely">...</select1> <textarea ref="who">...</textarea> <textarea ref="solution">...</textarea> </group>
In both cases, initially no answer has a selected choice: nothing will be displayed for the answers.
If the data for the selected answer doesn't have a likely
or
who
element, the controls for those elements won't be
displayed.
Slight change to the data to support this:
<data> <choice/> <answer choice="problem"> <problem/> <solution/> </answer> <answer choice="failure"> <problem/> <solution/> </answer> <answer choice="prediction"> <problem/> <likely/> <who/> <solution/> </answer> </data>
An advantage is that the form is no longer bound by the number of choices; the select just harvests that from the data:
<select1 ref="choice"> <label>How can you help?</label> <item><label>You know a problem that needs to be fixed</label> <value>problem</value></item> <item><label>You know a 'solution' that doesn't work</label> <value>failure</value></item> <item><label>You have a prediction about a future possible failure</label> <value>prediction</value></item> </select1>
⇒
<select1 ref="choice"> <label ref="instance('m')/choice/label"/> <itemset ref="instance('m')/choice/item"> <label ref="."/> <value ref="@value"/> </itemset> </select1>
The form is now driven from two data files: the template for the data, and the messages.
Essentially, the group of controls acts as an interpreter of the data.
For instance, add this to the data:
<answer choice="solution"> <problem/> <who/> <solution/> </answer>
along with suitable messages, and the form works without change with the new entry.
Good for content providers.
CSS presentation mode allows a different set of rules to apply when projecting on a screen.
@media projection { ... }
Advantages:
Alas, CSS presentation mode is now effectively dead.
This has caused the emergence of dozens of Javascript packages to do the same job, such as reveal, remark, webslides, deck, shwr, ...
Disadvantages:
Very easy: it is a surprisingly small amount of markup;
Allows the continued use and repurposing of existing content without change;
Continues to give the power of XHTML+CSS for styling.
Get the slides from some source, and create a bind that identifies what a slide is:
<instance id="slides" src="http://www.cwi.nl/~steven/Talks/2018/prague/"/> <bind id="slide" ref="h:body/h:div[position()=instance('i')/index]"/>
Using an admin instance:
<instance id="i"> <admin xmlns=""> <index>1</index> </admin> </instance>
Display one slide:
<group bind="slide"> ... </group>
Slide clicker remotes act like keyboards, and send the characters Page
Up
and Page Down
:
<action ev:event="keydown" ev:defaultAction="cancel"> <setvalue ref="instance('i')/index" if="event('key')='PageUp' or event('key')='ArrowLeft'" value=". - 1"/> <setvalue ref="instance('i')/index" if="event('key')='PageDown' or event('key')='ArrowRight'" value=". + 1"/> </action>
<group bind="slide"> <repeat ref="*"> <output class="h1" ref=".[name(.)='h1']"/> <output class="h2" ref=".[name(.)='h2']"/> <output class="pre" ref=".[name(.)='pre']"/> <group class="p" ref=".[name(.)='p'">... ... </repeat> </group>
Only one is matched, so only one is displayed.
Shorter:
<group bind="slide"> <repeat ref="*"> <output class="{name(.)}" ref=".[name(.)='h1' or name(.)='h2' or name(.)='pre']"/> <group class="p" ref=".[name(.)='p'">... ... </repeat> </group>
<group class="p" ref=".[name(.)='p']"> <repeat ref="node()"> <output class="text" ref=".[name(.)='#text']"/> <output class="{name(.)}" ref=".[name(.)='em' or name(.)='strong' or name(.)='code' or name(.)='a']"/> <output class="img" ref=".[name(.)='img']" value="concat(instance('i')/base,@src)" mediatype="image/*"/> </repeat> </group>
Such an approach is not a perfect replacement for CSS presentation mode, BUT
XForms 1 and 1.1 have a test-suite where each feature is tested by a separate file.
Difficult to maintain, update, and run; requires a lot of user interaction to see if tests have succeeded.
Naturally, the XForms 2 test suite is now a self-testing, data-driven form.
(This is work in progress; the example above is live, but may not always work).
We want to test that this does the right thing:
compare('apple', 'orange')
Enclose it:
<test>compare('apple', 'orange')</test>
mark the parameters:
<test>compare('<a>apple</a>', '<b>orange</b>')</test>
add attributes for the required result, actual result, and whether the test case passes:
<test pass="" res="" req="-1">compare('<a>apple</a>', '<b>orange</b>')</test>
<instance> <tests pass="" name="compare() function" xmlns=""> <description>...</description> <test pass="" res="" req="-1">compare(<a>apple</a>, <b>orange</b>)</test> <test pass="" res="" req="1">compare(<a>orange</a>, <b>apple</b>)</test> <test pass="" res="" req="0">compare(<a>apple</a>, <b>apple</b>)</test> ... </tests> </instance>
Test individual results (this is the only line that is different in any test
target, apart from the content of the test
instance):
<bind ref="test/@res" calculate="compare(../a, ../b)"/>
Check whether each passes:
<bind ref="test/@pass" calculate="if(../@res = ../@req, 'yes', 'no')"/>
Check whether the whole set passes:
<bind ref="@pass" calculate="if(test[@pass!='yes'], 'FAIL', 'PASS')"/>
These are also the same for each test set:
<group> <label class="title" ref="@name"/> <output class="block" ref="description"/> <output class="{@pass}" ref="@pass"/> <repeat ref="test"> <output value="."/> → <output ref="@res"/> <output class="wrong" value="if(@pass!='yes', concat(' expected: ', @req), '')"/> </repeat> </group>
Lots of flexibility.
Easy to add new tests.
Much easier to run.
Much easier to tell if a test passes or fails.
(Still work in progress.)
To aid in data-driven forms, the model is being enhanced in XForms 2.0.
Check out the XForms 2.0 spec at https://www.w3.org/community/xformsusers/wiki/XForms_2.0
Comparison of separating style and content was not accidental.
Data-driven forms add a level of abstraction to our documents, abstracting essence from the content.
Blurs the distinction between form and application.
A lot of power: allows declarative definition of applications that would normally be thought of as procedural.