Form. And Content

Steven Pemberton, CWI, Amsterdam

The author

New Technologies

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.

Books

A printed book imitating a manuscript

Cars

An early car

Apps

A diary app

Apps

An app

Digital Watches

A digital watch

Electronic Forms

e-TaxformForms 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.

What is a Form?

"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?

This is a form

A skeumorphic app

Form-driven data

Classically, a form drives the data:

The controls on the form dictate the structure of the underlying data being collected.

Data-driven forms

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:

XForms, a reminder

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"/>

Relevance

Here is an example (try it). If you select Credit card as payment type, the controls for that appear.

Source

Three examples

Data-driven Structure: A Questionnaire

A classic form.

Written for an internet community.

Form

Simplified from the original, this shows the essence (it is live, you can try it).

Source

Classic structure

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>

Cases

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 ...

Data

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>

Multilingual

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.

Replace

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.

New data

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>

Harvesting choices

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>

Analysis

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.

Data-driven Display: A Presentation Manager

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.

New presentations

This has caused the emergence of dozens of Javascript packages to do the same job, such as reveal, remark, webslides, deck, shwr, ...

Disadvantages:

Solution: Data-driven form

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.

Slides

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>

Controls

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>

One slide

<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>

More complicated case

<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>

Analysis

Such an approach is not a perfect replacement for CSS presentation mode, BUT

Source

Data-driven Control: The XForms 2.0 Test Suite

XForms 1 and 1.1 have a test-suite where each feature is tested by a separate file.

Source

Difficult to maintain, update, and run; requires a lot of user interaction to see if tests have succeeded.

XForms 2

Naturally, the XForms 2 test suite is now a self-testing, data-driven form.

Source

(This is work in progress; the example above is live, but may not always work).

The individual test cases are data-driven too

Source

Test case

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>

Many test cases, with optional description

<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>

Checking

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')"/>

Standard controls for all test sets

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>

Analysis

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.)

XForms 2: More data driven

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

Conclusion

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.