4

I have a repeat control with some data within it and a button that allows the user to increment the number of rows in the repeat. The problem is that I don't know how many times the user presses the button, so I don't know how many fields to create in a form. I could create like 100 fields but that wouldn't be a good practice. Is there a way to create a specific form from CSJS? Or how to bind the input fields from the repeat to some form fields in order to save the correct data from the repeat?

I binded it with EL like this:

<xp:repeat id="repeat1" var="test" indexVar="index"
            value="#{javascript:parseInt(sessionScope.dynaField)}" rows="8"
            style="border:1pt">


            <xp:table style="width:100.0%">
                <xp:tr>
                    <xp:td style="border:1pt;width:32.0%;text-align:center">
                        <xp:inputText id="inputText1" value="#{docrepeat[field1]}">


                            <xp:eventHandler event="onchange" submit="false">
                                <xp:this.script><![CDATA[try
{
var idx="view:_id1:inputText3";
var index=document.getElementById(idx).value;

var number="view:_id1:number";
var val=document.getElementById(number).value;

var sum = val;

for(var i=0;i<index;i++) {
var input1="view:_id1:repeat1:"+i+":inputText1"
var nr1=document.getElementById(input1).value;

sum-=nr1;
document.getElementById("view:_id1:repeat1:"+i+":inputText2").value = sum;
}

document.getElementById("view:_id1:test").value = (sum*100)/val; 

}
catch(e)
{
alert("no");
}]]></xp:this.script>
                            </xp:eventHandler>
                        </xp:inputText>
                        <xp:inputText id="inputText2">

                        </xp:inputText>
                    </xp:td>
                </xp:tr>
            </xp:table>
        </xp:repeat>

But I get a null pointer exception. Why?

P.S. Here is the entire page code:

<?xml version="1.0" encoding="UTF-8"?>
<xp:view xmlns:xp="http://www.ibm.com/xsp/core" xmlns:xp_1="http://www.ibm.com/xsp/coreex">

    <xp:this.beforePageLoad><![CDATA[#{javascript:sessionScope.dynaField=parseInt("1");}]]></xp:this.beforePageLoad>




    <xp:this.data>
        <xp:dominoDocument var="docrepeat" formName="repeat"></xp:dominoDocument>
    </xp:this.data>
    <xp:inputText id="inputText3" defaultValue="1">

        <xp:this.converter>
            <xp:convertNumber type="number" integerOnly="true"></xp:convertNumber>
        </xp:this.converter>
    </xp:inputText>

    <xp:inputText id="number" defaultValue="100">
        <xp:this.converter>
            <xp:convertNumber type="number" integerOnly="true"></xp:convertNumber>
        </xp:this.converter>
        <xp:eventHandler event="onchange" submit="true"
            refreshMode="partial" refreshId="repeat1">
            <xp:this.script><![CDATA[try
{
var idx="view:_id1:inputText3";
var index=document.getElementById(idx).value;

var number="view:_id1:number";
var val=document.getElementById(number).value;

var sum = val;

for(var i=0;i<index;i++) {
var input1="view:_id1:repeat1:"+i+":inputText1"
var nr1=document.getElementById(input1).value;

sum-=nr1;
document.getElementById("view:_id1:repeat1:"+i+":inputText2").value = sum;
}



}
catch(e)
{
alert("no");
}]]></xp:this.script>
        </xp:eventHandler>
    </xp:inputText>
    <xp:inputText id="test">
        <xp:this.converter>
            <xp:convertNumber type="number" integerOnly="true"
                locale="ro" maxFractionDigits="2">
            </xp:convertNumber>
        </xp:this.converter>
    </xp:inputText>
    <xp:panel id="Panel_All" style="text-align:center">

        <xp:pager layout="Previous Group Next" id="pager1" for="repeat1"
            panelPosition="top">
        </xp:pager>
        <xp:repeat id="repeat1" var="test" indexVar="index"
            value="#{javascript:parseInt(sessionScope.dynaField)}" rows="8"
            style="border:1pt">


            <xp:table style="width:100.0%">
                <xp:tr>
                    <xp:td style="border:1pt;width:32.0%;text-align:center">
                        <xp:inputText id="inputText1">
                            <xp:this.converter>
                                <xp:convertNumber type="number"></xp:convertNumber>
                            </xp:this.converter>
                            <xp:eventHandler event="onchange"
                                submit="false">
                                <xp:this.script><![CDATA[try
{
var idx="view:_id1:inputText3";
var index=document.getElementById(idx).value;

var number="view:_id1:number";
var val=document.getElementById(number).value;

var sum = val;

for(var i=0;i<index;i++) {
var input1="view:_id1:repeat1:"+i+":inputText1"
var nr1=document.getElementById(input1).value;

sum-=nr1;
document.getElementById("view:_id1:repeat1:"+i+":inputText2").value = sum;
}

document.getElementById("view:_id1:test").value = (sum*100)/val; 

}
catch(e)
{
alert("no");
}]]></xp:this.script>
                            </xp:eventHandler>
                        </xp:inputText>
                        <xp:inputText id="inputText2">
                            <xp:this.converter>
                                <xp:convertNumber type="number"></xp:convertNumber>
                            </xp:this.converter>
                        </xp:inputText>
                    </xp:td>
                </xp:tr>
            </xp:table>
        </xp:repeat>



        <xp:br></xp:br>


        <xp:br></xp:br>

        <xp:table style="width:100%">

            <xp:tr>
                <xp:td colspan="2" style="text-align:center">
                    <xp:button value="ADD FIELD" id="button1">
                        <xp:eventHandler event="onclick" submit="true"
                            refreshMode="complete">
                            <xp:this.action><![CDATA[#{javascript:sessionScope.dynaField=parseInt(sessionScope.dynaField)+1
getComponent("repeat1").setValue(parseInt(sessionScope.dynaField));
getComponent("inputText3").setValue(parseInt(sessionScope.dynaField));}]]></xp:this.action>
                        </xp:eventHandler>
                    </xp:button>
                    <xp:button value="REMOVE FIELD" id="button2">
                        <xp:eventHandler event="onclick" submit="true"
                            refreshMode="partial" refreshId="Panel_All">
                            <xp:this.action><![CDATA[#{javascript:sessionScope.dynaField=parseInt(sessionScope.dynaField)-1
getComponent("repeat1").setValue(parseInt(sessionScope.dynaField));
getComponent("inputText2").setValue(parseInt(sessionScope.dynaField));}]]></xp:this.action>

                        </xp:eventHandler>
                    </xp:button>
                </xp:td>

            </xp:tr>
        </xp:table>
        <xp:br></xp:br>
        <xp:button value="Diferenta" id="button4" style="width:200.0px">
            <xp:eventHandler event="onclick" submit="false">

                <xp:this.action>
                    <xp:executeScript>
                        <xp:this.script><![CDATA[#{javascript:getComponent("inputText3").setValue(getComponent("repeat1").getValue());}]]></xp:this.script>
                    </xp:executeScript>
                </xp:this.action>
            </xp:eventHandler>
        </xp:button>
    </xp:panel>

    <xp:button value="Save" id="button3">
        <xp:eventHandler event="onclick" submit="true"
            refreshMode="complete" immediate="false" save="true">
            <xp:this.action>
                <xp:openPage name="/xViewRepeat.xsp"></xp:openPage>
            </xp:this.action>
        </xp:eventHandler>
    </xp:button>
</xp:view>
Florin Pop
  • 5,105
  • 3
  • 25
  • 58

3 Answers3

2

You can save your repeat fields to document and restore from document this way:

  • collect all values in your input fields and join them with a char like "#" to one string
  • on submit write this string into a document field e.g. "RepeatFields"
  • on beforePageLoad read document field "RepeatFields", split it into an array and put it in a viewScope variable e.g. "viewScope.fields"
  • assign this viewScope variable to your repeat
  • connect your repeat input fields with an element of viewScope variable array with viewScope.fields[index]
  • re-size your viewScope variable array after ADD/REMOVE buttons were clicked in panel

This is a working sample XPage:

<?xml version="1.0" encoding="UTF-8"?>
<xp:view
    xmlns:xp="http://www.ibm.com/xsp/core">
    <xp:this.data>
        <xp:dominoDocument
            var="document1"
            formName="repeat">
        </xp:dominoDocument>
    </xp:this.data>
    <xp:this.beforePageLoad><![CDATA[#{javascript:
        var fields = [""];
        var repeatFields = document1.getItemValueString("RepeatFields");
        if (repeatFields) {
            fields = repeatFields.split("#");
        }
        viewScope.fields = fields;
        viewScope.dynaField = fields.length;
        }]]></xp:this.beforePageLoad>
    <xp:panel
        id="panelRepeat">
        <xp:this.rendered><![CDATA[#{javascript:    
            if (viewScope.fields.length < viewScope.dynaField) {
                viewScope.fields.push("");
            } else if (viewScope.fields.length > viewScope.dynaField) {
                viewScope.fields.remove(viewScope.fields.length - 1);
            }
            return true;}]]></xp:this.rendered>
        <xp:repeat
            id="repeat1"
            indexVar="index"
            value="#{viewScope.fields}"
            rows="100">
            <xp:inputText
                id="inputText1"
                value="#{viewScope.fields[index]}" />
            <xp:br />
        </xp:repeat>
    </xp:panel>
    <xp:br />
    <xp:button
        value="ADD FIELD"
        id="button1">
        <xp:eventHandler
            event="onclick"
            submit="true"
            refreshMode="partial"
            refreshId="panelRepeat">
            <xp:this.action><![CDATA[#{javascript:viewScope.dynaField += 1;
            }]]></xp:this.action>
        </xp:eventHandler>
    </xp:button>
    <xp:button
        value="REMOVE FIELD"
        id="button2">
        <xp:eventHandler
            event="onclick"
            submit="true"
            refreshMode="partial"
            refreshId="panelRepeat">
            <xp:this.action><![CDATA[#{javascript:viewScope.dynaField -= 1;
            }]]></xp:this.action>
        </xp:eventHandler>
    </xp:button>
    <xp:br />
    <xp:button
        value="Save"
        id="button3">
        <xp:eventHandler
            event="onclick"
            submit="true"
            refreshMode="complete"
            immediate="false"
            save="true">
            <xp:this.action><![CDATA[#{javascript:
             document1.replaceItemValue("RepeatFields", viewScope.fields.join('#'));
            }]]></xp:this.action>
        </xp:eventHandler>
    </xp:button>
</xp:view>

As an alternative you can save every input field into it's own document field. You will have a document field "NumberRepeatFields" which contains the number of input fields and document fields "RepeatField0", "RepeatField1", "RepeatField2", ... which contain the values.

<?xml version="1.0" encoding="UTF-8"?>
<xp:view
    xmlns:xp="http://www.ibm.com/xsp/core">
    <xp:this.data>
        <xp:dominoDocument
            var="document1"
            formName="repeat">
        </xp:dominoDocument>
    </xp:this.data>
    <xp:panel
        id="panelRepeat">
        <xp:inputText
            id="inputNumberFields"
            value="#{document1.NumberRepeatFields}"
            defaultValue="1"
            style="display: none;">
            <xp:this.converter>
                <xp:convertNumber
                    type="number"
                    integerOnly="true">
                </xp:convertNumber>
            </xp:this.converter>
        </xp:inputText>
        <xp:repeat
            id="repeat1"
            rows="1000"
            indexVar="index">
            <xp:this.value><![CDATA[#{javascript:
            var numberRepeatFields = document1.getItemValueInteger("NumberRepeatFields");
            return new Array(numberRepeatFields > 0 ? numberRepeatFields : 1);
            }]]></xp:this.value>
            <xp:repeat
                id="repeat2"
                var="fieldName"
                value="#{javascript:['RepeatField' + index]}">
                <xp:inputText
                    id="inputText1"
                    value="#{document1[fieldName]}">
                </xp:inputText>
                <xp:br />
            </xp:repeat>
        </xp:repeat>
    </xp:panel>
    <xp:br />
    <xp:button
        value="ADD FIELD"
        id="button1">
        <xp:eventHandler
            event="onclick"
            submit="true"
            refreshMode="partial"
            refreshId="panelRepeat">
            <xp:this.action><![CDATA[#{javascript:
            var numberRepeatFields = document1.getItemValueInteger("NumberRepeatFields");
            document1.replaceItemValue("NumberRepeatFields", numberRepeatFields + 1);
            }]]></xp:this.action>
        </xp:eventHandler>
    </xp:button>
    <xp:button
        value="REMOVE FIELD"
        id="button2">
        <xp:eventHandler
            event="onclick"
            submit="true"
            refreshMode="partial"
            refreshId="panelRepeat">
            <xp:this.action><![CDATA[#{javascript:
            var numberRepeatFields = document1.getItemValueInteger("NumberRepeatFields");
            if (numberRepeatFields > 1) {
                document1.replaceItemValue("NumberRepeatFields", numberRepeatFields - 1);
            }}]]></xp:this.action>
        </xp:eventHandler>
    </xp:button>
    <xp:br />
    <xp:button
        value="Save"
        id="button3">
        <xp:eventHandler
            event="onclick"
            submit="true"
            refreshMode="complete"
            immediate="false"
            save="true">
        </xp:eventHandler>
    </xp:button>
</xp:view>
Knut Herrmann
  • 30,880
  • 4
  • 31
  • 67
1

You don't have to create an underlying form. You can just create as many fields you want from XPages and refer to them from XPages.

When you are ready to look into Java, you could for instance use a MIME bean to store a Java structure of multiple fields. Tim Tripcony created a great video on this subject for NotesIn9: http://www.notesin9.com/2014/01/16/notesin9-135-using-java-in-xpages-part4/

Per Henrik Lausten
  • 21,331
  • 3
  • 29
  • 76
  • I need something like this: http://stackoverflow.com/questions/18258246/xpages-binding-to-edit-control-in-a-repeat But I get a nullPointer Exception – Florin Pop Sep 15 '14 at 07:33
1

Is there a specific reason for storing the data in fields you name on the main document? My approach would usually be to create one document for each row in the repeat. That will scale whether the user wants to create 1, 100, 1000, without design changes.

Paul Stephen Withers
  • 15,699
  • 1
  • 15
  • 33