Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Dynamic binding within a repeat control

Tags:

xpages

In a purchase order module, we need to ask certain questions depending on the source selection method, competition type and total cost of the PO. These questions are likely to change over time and in between different instances of the database.

So I have a view containing the questions, so that I can add questions dynamically to my XPage without needing to change the code. The answer to each question will be stored in a field. So, the document that contains the question has a field called FieldName that is used to supply the field name that will be used. Unfortunately, I am having no luck binding these dynamic fields to the document.

<xp:this.data>
    <xp:dominoView var="competitionQuestionView"
        viewName="CompetitionQuestions">
    </xp:dominoView>
</xp:this.data>
<xp:repeat id="repeat2" rows="30" var="rowData" style="width:700px"
    value="#{competitionQuestionView}">
    <xp:label id="label1">
        <xp:this.value><![CDATA[#{javascript:rowData.getColumnValue("Question");}]]></xp:this.value>
    </xp:label>
    <xp:inputText id="inputText1">
        <xp:this.rendered><![CDATA[#{javascript:rowData.getColumnValue("FieldType") == "Text Box"; }]]></xp:this.rendered>
        <xp:this.value><![CDATA[#{javascript:poDoc[rowData.getColumnValue ("FieldName")];}]]></xp:this.value>
    </xp:inputText>
</xp:repeat>

I've tried various ways to do this, including making a dynamicInputText custom control to pass in the field name, but without luck. The closest I got was when I used this:

<xp:this.value>
<![CDATA[#{javascript:tmp = rowData.getColumnValue ("FieldName");'#{poDoc.'+tmp+'}';}]]>
</xp:this.value>

That gave me something like #{poDoc.justification}, which was what I wanted to pass to the 'binding', but it ended up displaying as the text value.

I did try using $ to compute the value on load, but I am guessing that it didn't work because my (and the rowData) view is not available on load. That would eventually present a problem when I wanted to use partial refreshes due to updates on the criteria for which fields I want to display anyway.

Some of the answers to other questions looked promising, but no code was provided, so I couldn't figure it out.

like image 583
David Navarre Avatar asked Dec 26 '22 05:12

David Navarre


1 Answers

Behind the scenes, all data sources use the methods getValue and setValue to (respectively) read and write data. In the case of a Domino document data source, the expression #{currentDocument.fieldName} gets translated at runtime to either currentDocument.getValue('fieldName') or currentDocument.setValue('fieldName', postedValue), depending on whether the current operation is a read or a write.

If you set the value attribute of an otherwise editable component to a SSJS value binding, then it can't do this auto-translation... so it just treats every operation as a read.

In other words, for read/write to work, it has to be a "prefixless" expression.

There are several ways to handle this, but the easiest is to use a data context to map a SSJS expression to a single variable. Data contexts can be attached to the view root or to a panel, so in your example, you'd want to wrap your repeat contents in a panel:

<xp:repeat id="repeat2" rows="30" var="rowData" style="width:700px"
    value="#{competitionQuestionView}">
    <xp:panel>
        <xp:this.dataContexts>
            <xp:dataContext var="fieldName">
                <xp:this.value><![CDATA[#{javascript:rowData.getColumnValue ("FieldName");}]]></xp:this.value>
            </xp:dataContext>
        </xp:this.dataContexts>
        <xp:label id="label1">
            <xp:this.value><![CDATA[#{javascript:rowData.getColumnValue("Question");}]]>       </xp:this.value>
        </xp:label>
        <xp:inputText id="inputText1" value="#{poDoc[fieldName]}">
            <xp:this.rendered><![CDATA[#{javascript:rowData.getColumnValue("FieldType") == "Text Box"; }]]></xp:this.rendered>
        </xp:inputText>
    </xp:panel>
</xp:repeat>

So for each member of the repeat, the variable fieldName becomes the column value for that row. Then in the value attribute of the input component, the array syntax is used (instead of the usual dot syntax) since we want to use a variable to specify the field name instead of hardcoding the name.

In theory, however, you should be able to skip the data context entirely, and just set the following to be the value expression for the field:

#{poDoc[rowData.FieldName]}

In the context of the default ("prefixless") EL resolver, rowData.FieldName should return precisely the same value that rowData.getColumnValue("FieldName") returns in the context of a SSJS expression.

Finally, I would recommend reading this Expression Language tutorial to become familiar with all of the things that you can do in core EL without resorting to SSJS.

like image 152
Tim Tripcony Avatar answered Mar 02 '23 17:03

Tim Tripcony