1
votes

I'm using XSLTForms in eXist 2.2 with the RESTXQ server.

I have a search form that lets users query a remote API, which responds with a set of XML records if the query is matched. I am using xf:repeat to iterate over the records, and I want to be able to provide a checkbox at the head of each record, so that the user can choose the records they want. However, when I place a checkbox element inside xf:repeat (using xf:input bound to a boolean value), I don't get the desired functionality. Instead of being independent of each other, the checkboxes get activated as a group. When I click the first box, the second box is activated as well, etc. This seems as though it would be a common enough use case, but I can't seem to find any documentation or examples showing how to implement it.

I know I need to sync the two instances somehow, to ensure that there is a new bool element for each checkbox, and I have tried different approaches with xf:insert, but I can't get anything to work.

Model snippet:

<xf:instance xmlns="" id="default">
    <results>            
        <sru:record sru:test="false">
            <sru:recordData>
                <marc:record>
                ...
                </marc:record>
            </sru:recordData>
        </sru:record>
        <sru:record sru:test="false">
            <sru:recordData>
                <marc:record>
                ...
                </marc:record>
            </sru:recordData>
        </sru:record>                
    </results>
</xf:instance>

<xf:bind nodeset="instance('default')/sru:record/@sru:test" id="checkVal" type="xs:boolean"/>

Form snippet with xf:repeat:

<div>
    <xf:repeat 
    nodeset="instance('default')/sru:record/sru:recordData/marc:record" 
    id="marc-repeat" appearance="full">
        <div class="checkbox">
            <xf:input incremental="true" ref="../../@sru:test">
                <xf:label>Select</xf:label>      
                <xf:action ev:event="DOMActivate">                                    
                    <xf:setvalue 
                    bind="checkVal" 
                    if=". = boolean-from-string('false')">true</xf:setvalue>
                    <xf:setvalue 
                    bind="checkVal" 
                    if=". = boolean-from-string('true')">false</xf:setvalue>    
                </xf:action>                           
            </xf:input>
        </div>
        ...
    </xf:repeat>
</div>
2

2 Answers

4
votes

XForms allows to have the same node being bound to many controls and all of these controls, as a consequence, are synchronized on updates.

In your example, there is just one off element in a distinct instance. When an input control is bound to this element within a repeat, the XForms engine is rendering as many identical controls as nodes in the repeat node set.

I guess that you want to check records: for this, you need, for example, a dedicated attribute within each record. This can be done with XForms actions: each time the records instance is retrieved from the server, an associated action can insert such an attribute. With eXistdb, it might be easier to ask the server to generate the records instance with this extra attribute.

0
votes

I finally figured out that I just needed a nested xf:repeat to iterate through the set of records. I think my problem stemmed from a confusion about the functionality of the xf:bind element, which I ended up removing. With this revised form structure, the value of each @sru:test attributes now updates independently.

Form snippet with xf:repeat:

<div>
    <xf:repeat 
    nodeset="instance('default')/sru:record/sru:recordData" 
    id="marc-repeat" appearance="full">
        <div class="checkbox">
            <xf:repeat nodeset="marc:record">
                <xf:input incremental="true" ref="../../@sru:test">
                    <xf:label>Select</xf:label>      
                    <xf:action ev:event="DOMActivate">                                    
                        <xf:setvalue 
                        bind="checkVal" 
                        if=". = boolean-from-string('false')">true</xf:setvalue>
                        <xf:setvalue 
                        bind="checkVal" 
                        if=". = boolean-from-string('true')">false</xf:setvalue>    
                    </xf:action>                           
                </xf:input>
            </div>
            ...
    </xf:repeat>
</div>