Your browser doesn't support the features required by impress.js, so you are presented with a simplified version of this presentation.

For the best experience please use the latest Chrome, Safari or Firefox browser.

CC By 3.0Ce document est publié suivant une licence Creative Commons "Attribution 3.0 non transposé" (CC BY 3.0)
XML London

When MVC becomes a burden for XForms

Eric van der Vlist

vdv@dyomedea.com

Acknowledgments

Many thanks to

for their review and comments

Quiz

A quiz

Image © Wikimedia

1/3

A quiz

Image © Wikimedia

XForms quiz (1/3), question

Instance:

<figures>
   <line>
      <length>
         <value>10</value>
         <unit>in</unit>
      </length>
   </line>
</figures>

Expected result:

expected result

XForms quiz (1/3), answer

Model:

<xf:model>
   <xf:instance>
      <figures>
         <line>
            <length>
               <value>10</value>
               <unit>in</unit>
            </length>
         </line>
      </figures>
   </xf:instance>
</xf:model>

XForms quiz (1/3), answer

View:

<xf:group ref="line/length">
   <xf:input ref="value">
      <xf:label>Length: </xf:label>
   </xf:input>
   <xf:select1 ref="unit">
      <xf:label></xf:label>
      <xf:item>
         <xf:label>pixels</xf:label>
         <xf:value>px</xf:value>
      </xf:item>
      <xf:item>
         <xf:label>font size</xf:label>
         <xf:value>em</xf:value>
      </xf:item>
      .../...
      <xf:item>
         <xf:label>%</xf:label>
         <xf:value>%</xf:value>
      </xf:item>
   </xf:select1>
</xf:group>

XForms quiz (1/3), answer

Controller:

<!--No controller... yet!-->

2/3

two

Image © Wikimedia

XForms quiz (2/3), question

Instance:

<xf:instance id="main">
   <figures>
      <line length="10in"/>
   </figures>
</xf:instance>

Expected result:

expected result

XForms quiz (2/3), answer

Model:

<xf:model>
   <xf:instance id="main">
      <figures>
         <line length="10in"/>
      </figures>
   </xf:instance>
   <xf:instance id="split">
      <line>
         <length>
            <value/>
            <unit/>
         </length>
      </line>
   </xf:instance>
   .../...
</xf:model>

XForms quiz (2/3), answer

View:

<xf:group ref="instance('split')/length">
   <xf:input ref="value" id="length-control">
      <xf:label>Length: </xf:label>
   </xf:input>
   <xf:select1 ref="unit" id="unit-control">
      <xf:label/>
      <xf:item>
         <xf:label>pixels</xf:label>
         <xf:value>px</xf:value>
      </xf:item>
      .../...
      <xf:item>
         <xf:label>%</xf:label>
         <xf:value>%</xf:value>
      </xf:item>
   </xf:select1>
</xf:group>

XForms quiz (2/3), answer

Controller:

<xf:model>
   .../...
   <xf:action ev:event="xforms-ready">
      <xf:setvalue ref="instance('split')/length/value" 
          value="translate(instance('main')/line/@length, '%incmptxe', '')"/>
      <xf:setvalue ref="instance('split')/length/unit" 
          value="translate(instance('main')/line/@length, '0123456789', '')"/>
   </xf:action>
   <xf:action ev:event="xforms-value-changed" ev:observer="length-control">
      <xf:setvalue ref="instance('main')/line/@length" 
          value="concat(instance('split')/length/value, instance('split')/length/unit)"/>
   </xf:action>
   <xf:action ev:event="xforms-value-changed" ev:observer="unit-control">
      <xf:setvalue ref="instance('main')/line/@length" 
          value="concat(instance('split')/length/value, instance('split')/length/unit)"/>
   </xf:action>
</xf:model>

3/3

two

Image © Wikimedia

XForms quiz (3/3), question

Instance:

<xf:instance id="main">
   <figures>
      <rectangle height="10in" width="4em"/>
   </figures>
</xf:instance>

Expected result:

Same than previous, twice

Hint: Copy/paste is your friend

XForms quiz (3/3), answer

Model:

<xf:model>
   <xf:instance id="main">
      <figures>
         <rectangle height="10in" width="4em"/>
      </figures>
   </xf:instance>
   <xf:instance id="height">
      <height>
         <value/>
         <unit/>
      </height>
   </xf:instance>
   .../...
   <xf:instance id="width">
      <width>
         <value/>
         <unit/>
      </width>
   </xf:instance>
   .../...
</xf:model>

XForms quiz (3/3), answer

View:

<xf:group ref="instance('height')">
   <xf:input ref="value" id="height-value-control">
      <xf:label>Height: </xf:label>
   </xf:input>
   <xf:select1 ref="unit" id="height-unit-control">
      <xf:label/>
      <xf:item>
         <xf:label>pixels</xf:label>
         <xf:value>px</xf:value>
      </xf:item>
      .../...
   </xf:select1>
</xf:group>
<xh:br/>
<xf:group ref="instance('width')">
   <xf:input ref="value" id="width-value-control">
      <xf:label>Width: </xf:label>
   </xf:input>
   <xf:select1 ref="unit" id="width-unit-control">
      <xf:label/>
      <xf:item>
         <xf:label>pixels</xf:label>
         <xf:value>px</xf:value>
      </xf:item>
      .../...
   </xf:select1>
</xf:group>

XForms quiz (3/3), answer

Controller:

<xf:model>
   .../...
   <xf:action ev:event="xforms-ready">
      <xf:setvalue ref="instance('height')/value" 
        value="translate(instance('main')/rectangle/@height, '%incmptxe', '')"/>
      <xf:setvalue ref="instance('height')/unit" 
        value="translate(instance('main')/rectangle/@height, '0123456789', '')"/>
   </xf:action>
   <xf:action ev:event="xforms-value-changed" ev:observer="height-value-control">
      <xf:setvalue ref="instance('main')/rectangle/@height" 
        value="concat(instance('height')/value, instance('height')/unit)"/>
   </xf:action>
   <xf:action ev:event="xforms-value-changed" ev:observer="height-unit-control">
      <xf:setvalue ref="instance('main')/rectangle/@height" 
        value="concat(instance('height')/value, instance('height')/unit)"/>
   </xf:action>
   .../...
   <xf:action ev:event="xforms-ready">
      <xf:setvalue ref="instance('width')/value" 
        value="translate(instance('main')/rectangle/@width, '%incmptxe', '')"/>
      <xf:setvalue ref="instance('width')/unit" 
        value="translate(instance('main')/rectangle/@width, '0123456789', '')"/>
   </xf:action>
   <xf:action ev:event="xforms-value-changed" ev:observer="width-value-control">
      <xf:setvalue ref="instance('main')/rectangle/@width" 
        value="concat(instance('width')/value, instance('width')/unit)"/>
   </xf:action>
   <xf:action ev:event="xforms-value-changed" ev:observer="width-unit-control">
      <xf:setvalue ref="instance('main')/rectangle/@width" 
        value="concat(instance('width')/value, instance('width')/unit)"/>
   </xf:action>
</xf:model>

Homework

homework

Image © Wikimedia

XForms homework

Instance:

<xf:instance id="main">
   <figures>
      <rectangle height="10in" width="4em"/>
      .../...
      <rectangle height="20in" width="4%"/>
   </figures>
</xf:instance>

What if the element was repeated?

So what?

homework

Image © Wikimedia

The problem

Lack of "components" to package group of controls with their models, views and controllers.

There is nothing so practical as a good theory --Balisage

The MVC design pattern

Image © Wikimedia

MVC vs modularity

How can you reuse something split into three domains?

H(ierarchical)MVC

The HMVC design pattern

Image © JavaWorld

HMVC is what we need!

Components should have their own models, views and controllers!

Solutions

Solutions

Image © Wikimedia

Solution 1: copy/paste

duplicate

Image © Wikimedia

Copy/paste

Solution 2: generation or templating

duplicate

Image © Wikimedia

Generation or templating

Templating: model

<xf:model>
    <xf:instance id="main">
        <figures>
            <rectangle height="10in" width="4em"/>
        </figures>
    </xf:instance>
</xf:model>

Same as for XForms quiz (3/3)

Templating: view

<xh:body>
    <my:dimension ref="rectangle/@height">
        <xf:label>Height</xf:label>
    </my:dimension>
    <br/>
    <my:dimension ref="rectangle/@width">
        <xf:label>Width</xf:label>
    </my:dimension>
</xh:body>

<my:dimension> elements to be replaced by a transformation

Templating: generating the model

<xsl:template match="my:dimension" mode="model">
    <xsl:variable name="id" select="if (@id) then @id else generate-id()"/>
    <xf:instance id="{$id}-instance">
        <height>
            <value/>
            <unit/>
        </height>
    </xf:instance>
</xsl:template>

Templating: generating the view

<xsl:template match="my:dimension">
    <xsl:variable name="id" select="if (@id) then @id else generate-id()"/>
    <xf:group ref="instance('{$id}-instance')">
        <xf:input ref="value" id="{$id}-value-control">
            <xsl:apply-templates/>
        </xf:input>
        <xf:select1 ref="unit" id="{$id}-unit-control">
            <xf:label/>
            <xf:item>
                <xf:label>pixels</xf:label>
                <xf:value>px</xf:value>
            </xf:item>
            .../...
        </xf:select1>
    </xf:group>
</xsl:template>

Templating: generating the controller

<xsl:template match="my:dimension" mode="model">
    <xsl:variable name="id" select="if (@id) then @id else generate-id()"/>
    .../...
    <xf:action ev:event="xforms-ready">
        <xf:setvalue ref="instance('{$id}-instance')/value"
            value="translate(instance('main')/{@ref}, '%incmptxe', '')"/>
        <xf:setvalue ref="instance('{$id}-instance')/unit"
            value="translate(instance('main')/{@ref}, '0123456789', '')"/>
    </xf:action>
    <xf:action ev:event="xforms-value-changed" ev:observer="{$id}-value-control">
        <xf:setvalue ref="instance('main')/{@ref}"
            value="concat(instance('{$id}-instance')/value, instance('{$id}-instance')/unit)"/>
    </xf:action>
    <xf:action ev:event="xforms-value-changed" ev:observer="{$id}-unit-control">
        <xf:setvalue ref="instance('main')/{@ref}"
            value="concat(instance('{$id}-instance')/value, instance('{$id}-instance')/unit)"/>
    </xf:action>
</xsl:template>

Templating: not that easy

Solution(s) 3: XForms vendor extensions

duplicate

Image © Wikimedia

XForms vendor extensions

Solution 3.1: Orbeon's XBL components

orbeon

XBL components

XBL: caller model

<xf:model>
    <xf:instance id="main">
        <figures>
            <rectangle height="10in" width="4em"/>
        </figures>
    </xf:instance>
</xf:model>

Same as for templating's model

XBL: caller view

<xh:body>
    <my:dimension ref="rectangle/@height">
        <xf:label>Height</xf:label>
    </my:dimension>
    <br/>
    <my:dimension ref="rectangle/@width">
        <xf:label>Width</xf:label>
    </my:dimension>
</xh:body>

XBL: global structure

<xbl:binding id="my-dimension" element="my|dimension" xxbl:mode="lhha binding value">
   <xbl:handlers>
      .../...
   </xbl:handlers>
   <xbl:implementation>
      .../...
   </xbl:implementation>
   <xbl:template>
      .../...
   </xbl:template>
</xbl:binding>

XBL: component's model

<xbl:implementation>
   <xf:model id="my-dimension-model">
      <xf:instance id="my-dimension-instance">
         <dimension>
            <value/>
            <unit/>
         </dimension>
      </xf:instance>
      .../...
</xbl:implementation>

XBL: component's view

<xbl:template>
   <xf:input ref="value" id="my-dimension-value-control"/>
   <xf:select1 ref="unit" id="my-dimension-unit-control">
      <xf:label/>
      <xf:item>
         <xf:label>pixels</xf:label>
         <xf:value>px</xf:value>
      </xf:item>
      .../...
   </xf:select1>
</xbl:template>

XBL: component's controller (external)

<xbl:handlers>
   <xbl:handler event="xforms-enabled xforms-value-changed">
      <xf:setvalue ref="instance('my-dimension-instance')/value" 
          value="translate(xxf:binding('my-dimension'), '%incmptxe', '')"/>
      <xf:setvalue ref="instance('my-dimension-instance')/unit" 
          value="translate(xxf:binding('my-dimension'), '0123456789', '')"/>
   </xbl:handler>
</xbl:handlers>

XBL: component's controller (internal)

<xbl:implementation>
   <xf:model id="my-dimension-model">
      .../...
      <xf:setvalue ev:event="xforms-value-changed" ev:observer="my-dimension-value-control" 
          ref="xxf:binding('my-dimension')" 
          value="concat(instance('my-dimension-instance')/value, instance('my-dimension-instance')/unit)"/>
      <xf:setvalue ev:event="xforms-value-changed" ev:observer="my-dimension-unit-control" 
          ref="xxf:binding('my-dimension')" 
          value="concat(instance('my-dimension-instance')/value, instance('my-dimension-instance')/unit)"/>
   </xf:model>
</xbl:implementation>

XBL: summary

Solution(s) 3.2: Subforms

Russian dolls

Image © Wikimedia

Subforms

Solution(s) 3.2.1: betterFORM Subforms

betterFORM

Subforms are *not* components

Subforms are called subforms as they are *not* components ;)

-- Joern Turner on the betterform-users mailing list.

Wait till betterFORM 6

We will come up with our own component model in betterFORM 6 which will orient at more modern approaches (Web Components).

-- Joern Turner on the betterform-users mailing list.

betterFORM subforms

betterFORM id collision

betterFORM inter-model communication

betterFORM subform's model

<xf:model id="dimension-model">
    <xf:instance id="concat">
        <data/>
    </xf:instance>
    <xf:instance id="split">
        <height>
            <value/>
            <unit/>
        </height>
    </xf:instance>
    .../...
</xf:model>

betterFORM subform's view

<xf:group ref="instance('split')">
    <xf:input ref="value">
        <xf:action ev:event="xforms-value-changed">
            <xf:setvalue ref="instance('concat')" value="concat(instance('split')/value, instance('split')/unit)"/>
            <xf:send submission="set-dimension-value"/>
        </xf:action>
    </xf:input>
    <xf:select1 ref="unit">
        <xf:action ev:event="xforms-value-changed">
            <xf:setvalue ref="instance('concat')" value="concat(instance('split')/value, instance('split')/unit)"/>
            <xf:send submission="set-dimension-value"/>
        </xf:action>
        <xf:item>
            <xf:label>pixels</xf:label>
            <xf:value>px</xf:value>
        </xf:item>
        .../...
    </xf:select1>
</xf:group>

betterFORM subform's controller

<xf:model id="dimension-model">
    .../...
    <xf:submission id="get-dimension-value" resource="model:master#instance('dimension-interface')/*"
        replace="instance" method="get">
        <xf:action ev:event="xforms-submit-done">
            <xf:setvalue ref="instance('split')/value" value="translate(instance('concat'), '%incmptxe', '')"/>
            <xf:setvalue ref="instance('split')/unit" value="translate(instance('concat'), '0123456789', '')"/>
        </xf:action>
    </xf:submission>
    <xf:send ev:event="xforms-ready" submission="get-dimension-value"/>
    <xf:submission id="set-dimension-value" resource="model:master#instance('dimension-interface')/*" replace="none"
        method="post"/>
</xf:model>

betterFORM master model

<xf:model id="master">
    <xf:instance id="main">
        <figures>
            <rectangle height="10in" width="4em"/>
        </figures>
    </xf:instance>
    
    <!-- Instance used as an "interface" with the subform -->
    <xf:instance id="dimension-interface">
        <dimension active=""/>
    </xf:instance>
</xf:model>

betterFORM master view

<xf:group ref="rectangle">
    <!-- Height -->
    <xf:group ref="@height">
        <xf:label>Height: </xf:label>
        <!-- This should be displayed when the subform is not editing the height -->
        <xf:group ref=".[instance('dimension-interface')/@active!='height']">
            <xf:output ref="."/>
            <xf:trigger ref=".[instance('dimension-interface')/@active = '']">
                <xf:label>Edit</xf:label>
                <xf:action ev:event="DOMActivate">
                    <xf:setvalue ref="instance('dimension-interface')" value="instance('main')/rectangle/@height"/>
                    <xf:setvalue ref="instance('dimension-interface')/@active">height</xf:setvalue>
                    <xf:load show="embed" targetid="height" resource="subform-embedded.xhtml"/>
                </xf:action>
            </xf:trigger>
        </xf:group>
        <xh:div id="height"/>
        <!-- This should be displayed only when we're editing the height -->
        <xf:group ref=".[instance('dimension-interface')/@active='height']">
            <xf:trigger>
                <xf:label>Done</xf:label>
                <xf:action ev:event="DOMActivate">
                    <xf:setvalue value="instance('dimension-interface')" ref="instance('main')/rectangle/@height"/>
                    <xf:setvalue ref="instance('dimension-interface')/@active"/>
                    <xf:load show="none" targetid="height"/>
                </xf:action>
            </xf:trigger>
        </xf:group>
    </xf:group>
    <br/>
    <!-- Width -->
    <xf:group ref="@width">
        .../...
    </xf:group>
</xf:group>

betterFORM subforms: summary

Solution(s) 3.2.2: XSLTForms subforms

XSLTForms

XSLTForms subforms

Porting from betterFORM to XSLTForms

Porting our example from betterFORM to XSLTForms is not that difficult (see my paper)

A gift from XSLTForms to XML London

I have implemented a new component control in XSLTForms. It is named "xf:component" and has two attributes named "@ref" and "@resource".

-- Alain Couthures on the Xsltforms-support mailing list.

XSLTForms' xf:component

XSLTForms component's extension functions

XSLTForms component's model

<xf:model>
    <xf:instance>
        <size>
            <value xsi:type="xsd:decimal">2</value>
            <unit>cm</unit>
        </size>
    </xf:instance>
    <xf:bind ref="subform-instance()/value" 
        changed="translate(subform-context(), '%incmptxe', '')"/>
    <xf:bind ref="subform-instance()/unit" 
        changed="translate(subform-context(), '0123456789', '')"/>
</xf:model>

XSLTForms component's view

<xh:body>
    <xf:input ref="subform-instance()/value">
        <xf:label/>
        <xf:setvalue ev:event="xforms-value-changed" 
            ref="subform-context()" value="concat(subform-instance()/value, 
            subform-instance()/unit)"/>
    </xf:input>
    <xf:select1 ref="subform-instance()/unit">
        <xf:label/>
        <xf:item>
            <xf:label>pixels</xf:label>
            <xf:value>px</xf:value>
        </xf:item>
        .../...
        <xf:setvalue ev:event="xforms-value-changed" 
            ref="subform-context()" value="concat(subform-instance()/value, 
            subform-instance()/unit)"/>
    </xf:select1>
</xh:body>

XSLTForms component master form model

<xf:model>
    <xf:instance id="main">
        <figures>
            <rectangle height="10in" width="4em"/>
        </figures>
    </xf:instance>
</xf:model>

XSLTForms master form view

<xf:group ref="rectangle">
    <!-- Height -->
    <xf:group ref="@height">
        <xf:label>Height: </xf:label>
        <xf:component ref="." resource="component-subform.xml"/>
    </xf:group>
    <br/>
    <!-- Width -->
    <xf:group ref="@width">
        <xf:label>Width: </xf:label>
        <xf:component ref="." resource="component-subform.xml"/>
    </xf:group>
</xf:group>

XSLTForms subforms: summary

The end

The end

Image © Wikimedia

Conclusion / Summary

Future

Use a spacebar or arrow keys to navigate