0

I have an xml file that I try to describe some simple user-interface and then transform it via XSLT to a valid html with XForms inside. My intention is that this is just as abstract as possible and should not contain logic. But with XForms you can do binding between several controls easily. I am trying to achieve this with a XSL transformation over this document:

<structure>            
    <part class="TextInput" id="name"/>
    <part class="TextOutput" id="nameOutput"/>
</structure>
<style>
    <property part-name="name" name="label">Enter your name:</property>
    <property part-name="nameOutput" name="label">Output name:</property>
</style>
<behavior>
    <rule>
        <condition>
            <event part-name="name" class="binding"/>                 
        </condition>
        <action>
            <property part-name="name" bind="name"/>
        </action>
    </rule>
    <rule>
        <condition>
            <event part-name="nameOutput" class="binding"/>                 
        </condition>
        <action>
            <property part-name="nameOutput" bind="name"/>                        
        </action>
    </rule>
</behavior>

So basically, I describe it as such - I have a rule that says which part will be bound to some other part (all parts have unique ids - in this case name and nameOuput). In other words, in this xml I have 2 parts and I want to add an attribute into each of them that will define the possible binding. This is what I cannot figure out how to do...

This should be the valid output:

<html
   xmlns="http://www.w3.org/1999/xhtml"
   xmlns:xf="http://www.w3.org/2002/xforms" 
   xmlns:xs="http://www.w3.org/2001/XMLSchema" 
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
   <head>
      <title>Test</title>
      <xf:model>
         <xf:instance xmlns="">
            <data>
               <name/>
            </data>
         </xf:instance>
         <xf:bind id="name" nodeset="/data/name"/>
      </xf:model>
   </head>
   <body>
       <xf:input id="name" ref="name" bind="name">
          <xf:label>Enter your name:</xf:label>
       </xf:input>
       <xf:output id="name" ref="name" bind="name">
          <xf:label>Output name:</xf:label>
       </xf:output>
   </body>
</html>

And I currently get up to here:

.......
<body>
    <xf:input id="name" ref="name">
        <xf:label>Enter your name:</xf:label>
    </xf:input>
    <xf:output id="name" ref="name">
         <xf:label>Output name:</xf:label>
    </xf:output>
</body>
......

I achieve everything else, except the part where I have to include the instance that has to be bound to the <xf:input> and <xf:output> elements as attributes (<xf:input bind="name">). So as you can see, the bind="part-name" attribute is missing.

I have distributed my XSLTs in different files for readability/maintainability, but here is the one that is responsible for transforming to <xf:input> only - I guess it should be enough, but I can share others if needed as well.

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    xmlns:xf="http://www.w3.org/2002/xforms" 
    exclude-result-prefixes="xs"
    version="2.0">    
    <xsl:key name="textLabels" match="property[@name='label']" use="@part-name"/>
    <xsl:template match="part[@class='TextInput'][key('textLabels', @id)]">
        <xf:input>
            <xsl:apply-templates select="@size | @style | @id"/>
            <!-- Create Reference to the XForms instance -->
            <xsl:attribute name="ref">
                <xsl:choose>
                    <xsl:when test="@id">
                        <xsl:value-of select="@id"/>
                    </xsl:when>
                    <xsl:when test="@name and not(@id)">
                        <xsl:value-of select="@name"/>
                    </xsl:when>
                    <xsl:otherwise>
                        <xsl:value-of select="concat(local-name(.), position())"/>
                    </xsl:otherwise>
                </xsl:choose>
            </xsl:attribute>            
            <xsl:for-each select="behavior/rule/condition/event[@class='binding']">
                <xsl:attribute name="bind">
                    <xsl:value-of select="@part-name"/>
                </xsl:attribute>
            </xsl:for-each>
            <xf:label>
                <xsl:value-of select="key('textLabels', @id)"/>
            </xf:label>
        </xf:input>
    </xsl:template>    
</xsl:stylesheet>

So how can I add the value of the attribute in behavior/rule/action/property[@bind]? Thanks in advance :)

1 Answer 1

1

I think the problems lie with this line

 <xsl:for-each select="behavior/rule/condition/event[@class='binding']">

These leads to two problems. Firstly, it is a relative xpath expression; relative to the node on which you are positioned. In this case, the part element. But behaviour is not a child of part so the expression will not match anything!

Secondly, you are looking at all rule elements, when I think you only need to be finding the one with the matching part-name for the binding.

To solve this, what you could do, is first define a key to look up rule elements by the binding part-name

<xsl:key name="bindings" match="rule" use="condition/event[@class='binding']/@part-name"/>

Then, to create your bind attribute on the xf:input element, simply replace the existing xsl:for-each with this:

  <xsl:attribute name="bind">
      <xsl:value-of select="key('bindings', @id)/action/property/@bind"/>
  </xsl:attribute>
Sign up to request clarification or add additional context in comments.

1 Comment

Yes, that solved it for me. I was trying with defying a key as well, but unsuccessfully. Thanks for the swift reply!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.