I'm using Mark Hughes picklist on my xpage which is using a datasource. The view from which I pick the values is listing documents having another datasource.
I put the selected value into an <xp:inputText>. I do want to create a link which should redirect me to the listed document from the view ( from it I took the value ). In other words, I do want to find out the UNID of the document from the view, which I did selected it.
I tried the following code for the ssjsSelectFunction:
var unid = viewScope.unid;
if(typeof unid != "undefined" && unid != null)
{
var doc = database.getDocumentByUNID(unid);
var val1 = doc.getItemValueString("txt_numeAcord_1");
var val2 = doc.getUniversalID();
getComponent("inputText24").setValue(val1);
getComponent("inputText25").setValue(val2);
}
But after selecting the desired doc. from the picklist, only inputText25 is updated with the value ( UNID ), the inputText24 is empty. Only if I open again the picklist and select the doc., the inputText24 field value is added. I guess I'm missing something.
How can I achieve this?
My xpage code:
<?xml version="1.0" encoding="UTF-8"?>
<xp:view xmlns:xp="http://www.ibm.com/xsp/core"
xmlns:xc="http://www.ibm.com/xsp/custom"
xmlns:xe="http://www.ibm.com/xsp/coreex" >
<xp:this.data>
<xp:dominoDocument var="Contr" formName="(fmFormularCIP)"></xp:dominoDocument>
<xp:dominoView var="view1" viewName="vwAcord"></xp:dominoView>
</xp:this.data>
<xp:panel id="AcordCadru">
<xp:br></xp:br>
<xp:checkBox text="Acord cadru" id="checkBox6"checkedValue="Da" uncheckedValue="Nu" value="#{Contr.chkAcord}">
<xp:eventHandler event="onchange" submit="true"
refreshMode="partial" refreshId="AcordCadru">
</xp:eventHandler>
</xp:checkBox>
<xp:inputText id="inputText24" value="#{Contr.acord}">
</xp:inputText>
<xc:viewpicklist rowsPerPage="10"
buttonImage="./folder_explore.png" tableClass="tablecellgreen"
headerClass="headerclass" rowClass="odd, even" searchBar="false"
searchButtonText="Search" searchButtonClass="button2"
searchBarClass="headerclass" pagerStyleFirst="navbutton1"
pagerStylePrevious="navbutton2" pagerStyleCurrent="navbutton4"
pagerStyleNext="navbutton2" pagerStyleLast="navbutton3"
typeAheadBar="false" select="UNID" onReturn="Set Scope Value"
bottomBarClass="bottomround headerclass" cancelButtonText="Cancel"
cancelButtonClass="button2 floatthisright" type="Single Value"
finishButtonText="Finish" finishButtonClass="button2 floatthisright"
multiSelectButtonAddImg="./add.png"
multiSelectButtonRemoveImg="./delete.png"
picklistButtonClass="button" openDialogWith="Button"
picklistLinkImg="./add.png" multiSelectLinkAddImg="./add.png"
multiSelectLinkRemoveImg="./delete.png" selectWith="Button"
multiValueSeparator="," clearSearchImg="./cross.png"
SelectCellWidth="30px" dialogID="dialog1"
dialogTitle="Alegeti nr. acord cadru" fieldName="inputText24"
refreshID="AcordCadru" datasrc="view1" selectColumn="0"
varName="viewScope.unid">
<xc:this.viewColumn>
<xp:value>0</xp:value>
<xp:value>1</xp:value>
<xp:value>2</xp:value>
<xp:value>3</xp:value>
<xp:value>4</xp:value>
</xc:this.viewColumn>
<xc:this.ssjsSelectFunction><![CDATA[#{javascript:
var unid = viewScope.unid;
if(typeof unid != "undefined" && unid != null)
{
var doc = database.getDocumentByUNID(unid);
var val1 = doc.getItemValueString("txt_numeAcord_1");
var val2 = doc.getUniversalID();
Contr.setValue("acord",val1);
Contr.setValue("sv",val2);
}}]]></xc:this.ssjsSelectFunction>
</xc:viewpicklist>
<xp:br></xp:br>
<xp:inputText id="inputText25" value="#{Contr.sv}">
</xp:inputText>
</xp:panel>
</xp:view>
inputText24 doesn't get the selected value because the execution of ssjsSelectFunction's code is too late. It gets executed during the refresh of panel "AcordCadru" caused by parameter refreshID. All fields positioned in front of xc:viewpicklist get refreshed before ssjsSelectFunction's code execution. That's why inputText24 doesn't get the new selected value but inputText25 which comes after xc:viewpicklist does.
If you put inputText24 behind xc:viewpicklist then it will get the new selected value.
But, probably you want to have field inputText24 first and the picklist button xc:viewpicklist after. For this
delete the ssjsSelectFunction code
add a computed field in front of inputText24 with pretty the same code
<xp:text
escape="true"
id="computedField1">
<xp:this.value><![CDATA[#{javascript:
var unid = viewScope.unid;
if (unid && unid !== Contr.sv) {
var doc = database.getDocumentByUNID(unid);
var val1 = doc.getItemValueString("txt_numeAcord_1");
Contr.setValue("acord",val1);
Contr.setValue("sv",unid);
}
return "";
}]]></xp:this.value>
</xp:text>
It will set the new selected values to document and made them visible in inputText24 and inputText25 right away.
Related
I have some code, that if a user selects a "NO" answer on a certain field (Q1), it removes the answers of sub question related to Q1, and also hides them in the UI.
This works fine, and I do a partial refresh on the div containing all the questions. However, when I then click on the same question selecting answer "Yes" which then shows all the sub questions, so the user can answer them again, they still have the old values attributed.
When I remove the fields, the backend document reflects this correctly, but then when I click the question which then shows the fields again in the UI, they appear in the backend document again, with their original value. If I do a context.reloadPage() at the end of my code, everything works as expected, but this breaks other functionality and I only want a partial refresh. Any ideas? Thanks
try{
if (compositeData.onClickCode) {
compositeData.onClickCode.invoke( facesContext, null );
}
document1.save();
var fieldName:string = compositeData.fieldName;
//Blank all IFM fields if user selects no IFM *CHANGE FIELDS ARRAY TO GET FROM KEYWORD*
if (fieldName == "Q1") {
print("Yes Q1");
if(document1.getItemValueString(fieldName)=="No") {
print("NO IFMS");
var questionsIFM = ["Q1a", "Q1b", "Q1c", "Q3IFM", "Q3bIFM", "Q3cIFM", "Q3dIFM", "Q4", "Q4a" ,"Q5IFM", "Q6IFM", "Q6aIFM", "Q7IFM",
"Q7aIFM", "Q7bIFM", "Q8", "Q8a", "Q9IFM", "Q9aIFM", "Q9bIFM", "Q10IFM", "Q11IFM"];
var len = questionsIFM.length;
for (i = 0; i < len; i++) {
print("Field Name: " + questionsIFM[i]);
document1.removeItem(questionsIFM[i]);
}
document1.save();
//context.reloadPage();
}
}
var guidanceOptions:string = "";
if (compositeData.guidanceOptions) {
if(#IsMember(document1.getItemValueString(fieldName), compositeData.guidanceOptions)){
guidanceOptions = document1.getItemValueString(fieldName);
}
}
viewScope.guidance = compositeData.fieldName+guidanceOptions;
}catch(e){
openLogBean.addError(e,this.getParent());
}
Instead of changing the backend document you could update the datasource with doc.setValue('fieldName','')
Alternativly, you could change the scope of the datasource to "request", this forces the datasource to reload the backend document for every request.
EDIT:
Here is an example for updating the datasource directly (Button 'Clear') which always works and updating the component (Button 'Update').
If the button 'Reload' is clicked, changes to in the fields are "stored" in the component. The button 'Update' does a partial refresh (which hides the input field), so the value is lost.
<?xml version="1.0" encoding="UTF-8"?>
<xp:view
xmlns:xp="http://www.ibm.com/xsp/core">
<xp:this.data>
<xp:dominoDocument
var="document1"></xp:dominoDocument>
</xp:this.data>
<xp:div
id="refreshMe">
<xp:inputText
id="inputText1"
value="#{document1.foo}"></xp:inputText>
<xp:inputText
id="inputText2"
value="#{document1.bar}"
rendered="#{javascript:facesContext.isAjaxPartialRefresh() == false}">
</xp:inputText>
</xp:div>
<xp:button
value="Update"
id="buttonUpdate">
<xp:eventHandler
event="onclick"
submit="true"
refreshMode="partial"
refreshId="refreshMe">
</xp:eventHandler>
</xp:button>
<xp:button
value="Clear"
id="buttonClear">
<xp:eventHandler
event="onclick"
submit="true"
refreshMode="partial"
refreshId="refreshMe">
<xp:this.action><![CDATA[#{javascript:document1.setValue('foo', '');document1.setValue('bar', '')}]]></xp:this.action>
</xp:eventHandler>
</xp:button>
<xp:button
value="Reload"
id="buttonReload">
<xp:eventHandler
event="onclick"
submit="true"
refreshMode="complete">
</xp:eventHandler>
</xp:button>
</xp:view>
I might be late to this party, but I found a way to force a reload from the back-end document. All you have to do is change the id for the dataSource a little, by adding or removing a "0" prefix to the documentId. Something like this:
return (viewScope.idPrefix? "0":"") + param.documentId
and when you want to force a reload during a partial refresh, just set
viewScope.idPrefix= !viewScope.idPrefix;
Don't forget to set ignoreRequestParams to true.
I have a xpage that contains 4 edit boxes and a button, they are used to export the data in excel. I use session scope in those four edit boxes because I have store the value in order to generate the excel.
The design look looks like the following:
//1st edit box: get the user name, set it to default so the user does not need to type his/her name
<xp:inputText id="inputText1" value="#{sessionScope.User}">
<xp:this.defaultValue><![CDATA[#{javascript: #Name("[CN]",#UserName())}]]></xp:this.defaultValue>
</xp:inputText>
//2nd edit box: after get the user name in edit box 1, retrieve the user's department in this edit box
<xp:inputText id="inputText2"value="#{sessionScope.Department}">
<xp:this.defaultValue><![CDATA[#{javascript:#DbLookup(#DbName(),"UserView", (getComponent("inputText1").getValue()),2 )}]]></xp:this.defaultValue>
</xp:inputText>
//3rd edit box: get the start search date in YYYYMM format, user only can type numbers
<xp:inputText id="inputText3" value="#{sessionScope.startYYYYMM}">
<xp:eventHandler event="onkeypress" submit="false">
<xp:this.script><![CDATA[if (event.keyCode >= 48 && event.keyCode <= 57)
{
event.returnValue = true;
}
else
{
event.returnValue = false;
}]]></xp:this.script>
<xp:this.parameters>
<xp:parameter name="message1" value="value">
/xp:parameter>
</xp:this.parameters>
</xp:eventHandler>
</xp:inputText>
//4th editbox: get the endsearch date in YYYYMM format, user only can type numbers
<xp:inputText id="inputText4" value="#{sessionScope.startYYYYMM}">
<xp:eventHandler event="onkeypress" submit="false">
<xp:this.script><![CDATA[if (event.keyCode >= 48 && event.keyCode <= 57)
{
event.returnValue = true;
}
else
{
event.returnValue = false;
}]]></xp:this.script>
<xp:this.parameters>
<xp:parameter name="message1" value="value">
/xp:parameter>
</xp:this.parameters>
</xp:eventHandler>
</xp:inputText>
//a button to call another page to export the excel
<xp:button value="Export" id="button11">
<xp:eventHandler event="onclick" submit="true" immediate="false" save="false" refreshMode="complete">
<xp:this.action><![CDATA[#{javascript:context.redirectToPage("ExportExcel.xsp");}]]></xp:this.action>
</xp:eventHandler>
</xp:button>
I run the code, it works fine. However here is what I would like to ask. In the first and the second edit box, if I set those edit boxes to Read-only. It seems lose the session scope variable and I run the code, the excel just contains the default header.
I find this post ReadOnly field in Xpage not submitted is very similar to my case, so I set the edit box to editable and try to apply this code from the post:
//for 1st editbox
<xp:scriptBlock id="scriptBlock1">
<xp:this.value><![CDATA[function makeFieldReadOnly() {
document.getElementById("#{id:inputText1}").readOnly = true;
}
window.onload = makeFieldReadOnly;]]></xp:this.value>
</xp:scriptBlock>
//for 2nd edit box
<xp:scriptBlock id="scriptBlock2">
<xp:this.value><![CDATA[function makeFieldReadOnly() {
document.getElementById("#{id:inputText2}").readOnly = true;
}
window.onload = makeFieldReadOnly;]]></xp:this.value>
</xp:scriptBlock>
The edit box still editable and the excel is not work properly (only display the default header).
So how can I make the edit box read-only and still have the session scope variable?
Grateful for your advice please. Thank you.
Reference:
ReadOnly field in Xpage not submitted
export data from panel
You can calculate the values for your two sessionScope variables in e.g. the beforeRenderResponse:
<xp:this.beforeRenderResponse><![CDATA[#{javascript:
sessionScope.User = #Name("[CN]",#UserName());
sessionScope.Department = #DbLookup(#DbName(),"UserView", (#Name("[CN]",#UserName()),2 )
}]]></xp:this.beforeRenderResponse>
You can then show the values of the sessionScope variables in computed text fields instead of input fields:
<xp:text escape="true" id="computedField1" value="#{sessionScope.User}" />
<xp:text escape="true" id="computedField2" value="#{sessionScope.Department}" />
I had this issue with 9.01 FP6
The best solution was to place the inputs in a xp:panel and then use the readonly attribute on the panel to make the field within it read only.
This worked well and was easy to implement
I have a custom control that contains 2 images: add and remove from favorites.
There is some code on the onCLick event of the image, and both images use a SSJS function to see if the current document is already in the favorites or not, in the visible property of each image.
All works well, execpt that I need to click twice on the image in order to see the changes in the UI. Both onClick events are set to FullUpdate (also tried partial update with the panel that contains the images).
I could move all the favorites logic into session scope variables, but I think this should work as is. I just don't understand why I need to click twice, as if the partial refresh doesn't do anything (though it is, as I see the reload Arrow in the browser!).
Can it be that the code takes too long to execute and the refresh doesn'T get the updated info???
Here's the custom control code:
<?xml version="1.0" encoding="UTF-8"?>
<xp:view xmlns:xp="http://www.ibm.com/xsp/core" xmlns:xe="http://www.ibm.com/xsp/coreex">
<xp:this.resources>
<xp:script src="/AppUtils.jss" clientSide="false"></xp:script>
</xp:this.resources>
<xp:image url="/favorites-add.png" id="image1"
alt="Add to Favorites" title="Add to Favorites">
<xp:this.rendered><![CDATA[#{javascript:!isInFavorites(document1.getDocument().getUniversalID(), userBean.canonicalName);
}]]></xp:this.rendered>
<xp:eventHandler event="onclick" submit="true" refreshMode="complete">
<xp:this.action><![CDATA[#{javascript:addToFavorites(document1.getDocument().getUniversalID(),userBean.canonicalName);}]]></xp:this.action>
</xp:eventHandler>
</xp:image>
<xp:image url="/favorites-remove.png" id="image2"
alt="Remove from Favorites" title="Remove from Favorites">
<xp:this.rendered><![CDATA[#{javascript:isInFavorites(document1.getDocument().getUniversalID(),userBean.canonicalName);
}]]></xp:this.rendered>
<xp:eventHandler event="onclick" submit="true"
refreshMode="complete">
<xp:this.action><![CDATA[#{javascript:removeFromFavorites(document1.getDocument().getUniversalID(),userBean.canonicalName);}]]></xp:this.action>
</xp:eventHandler>
</xp:image>
</xp:view>
And here's the SSJS:
function addToFavorites(unid, userName) {
var newDoc:NotesDocument = database.createDocument();
newDoc.appendItemValue("Form","Favorite");
newDoc.appendItemValue("UserName", userName);
newDoc.appendItemValue("DocUNID", unid);
newDoc.save();
return;
}
function removeFromFavorites(unid, userName) {
var notesView:NotesView = database.getView("(lkpFavorites)");
var keys = new java.util.Vector();
keys.addElement(userName);
keys.addElement(unid);
var coll:NotesDocumentCollection = notesView.getAllDocumentsByKey(keys, true);
if(coll.getCount()==1) {
coll.getFirstDocument().remove(true);
}
return;
}
function isInFavorites(unid, userName) {
var notesView:NotesView = database.getView("(lkpFavorites)");
var keys = new java.util.Vector();
keys.addElement(userName);
keys.addElement(unid);
var coll:NotesDocumentCollection = notesView.getAllDocumentsByKey(keys, true);
if(coll.getCount()>0) {
return true;
} else {
return false;
}
}
I'd suggest you to put an xp:link around your image to cast the event code instead of using the evennts of the image directly.
OK, not sure what happened there but I manually edited the custom control's source to see wheter I had empty eventHandler as suggested by Oliver, and it started to work as expected. I am totally unsure of what I changed: to my knowledge, all I did was to add extra "returns" in the source view, to make it more readable... Christmas gift I guess.
All is good now. Thanks to all :)
I'm using a repeat control displaying names. The content is displayed proberly, but I needed a pager because there can be more than 30 entries.
As soon as I add a pager for this repeat control and press a button doing a partial refresh on the site I get a "division by zero" error.
Here's my repeat control:
<xp:repeat id="repeat1" rows="30" var="namesList" repeatControls="true" indexVar="rowIndex">
<xp:table>
<xp:tr>
<xp:td>
<xp:link escape="true" id="Employee">
<xp:this.text><![CDATA[#{javascript:namesList.getItemValueString("Employee")}]]></xp:this.text></xp:link>
</xp:td>
</xp:tr>
</xp:table>
<xp:this.value><![CDATA[#{javascript:try {
var wfDoc:NotesDocument = docApplication.getDocument(true);
var dcNoAnswer:NotesDocumentCollection = database.createDocumentCollection();
var count:Integer = 0;
//Gets all response documents, but only adds those to the document collection, without a valid
//response to the cycle
if (wfDoc.getResponses() != null) {
var dc:NotesDocumentCollection = wfDoc.getResponses();
var doc:NotesDocument = dc.getFirstDocument();
while (doc != null) {
if (doc.getItemValueString("Response") == "") {
dcNoAnswer.addDocument(doc);
}
doc = dc.getNextDocument();
}
dc.recycle();
}
docApplication.replaceItemValue("MissingResponsesCount", dcNoAnswer.getCount());
wfDoc.recycle();
return dcNoAnswer;
} catch(e) {
dBar.error("Show no response repeat control data: " + e);
}}]]></xp:this.value>
</xp:repeat>
And here's my pager:
<xp:pager layout="Previous Group Next" id="pager3" for="repeat1"></xp:pager>
As I said, doing a partial refresh on the site oder panel, I'll get this error:
I've been looking for an error for some time, but I can't see anything wrong here. Did anybody else have such a problem and maybe has a solution?
You have repeatControls="true" set. That means "create the controls once and never again", so you can't use a pager to navigate, as mentioned here. Remove repeatControls="true" and it will work.
If you look at the stack trace, it will fail on getStart().
Try adding alwaysCalculateLast="false" to your pager. The repeat control could have 0 values (because of dcNoAnswer) and maybe you have set xsp.repeat.allowZeroRowsPerPage=true in your xsp properties settings.
I have a xpage that use view.postscript like this:
<?xml version="1.0" encoding="UTF-8"?>
<xp:view xmlns:xp="http://www.ibm.com/xsp/core">
<xp:this.resources>
<xp:dojoModule name="dojox.widget.Toaster"></xp:dojoModule>
<xp:styleSheet href="/.ibmxspres/dojoroot/dojox/widget/Toaster/Toaster.css"></xp:styleSheet>
</xp:this.resources>
<xp:scriptBlock id="scriptBlock1">
<xp:this.value><![CDATA[var Toaster = function(id, msg, type, duration, pos) {
type = (type == null) ? "message" : type;
duration = (duration == null) ? "5000" : duration;
pos = (pos == null) ? "br-up" : pos;
var obj = dijit.byId(id);
obj.positionDirection = pos;
obj.setContent(msg, type, duration);
obj.show();
}]]></xp:this.value>
</xp:scriptBlock>
<xp:div id="toaster" themeId="toaster" dojoType="dojox.widget.Toaster"></xp:div>
<xp:button value="Toaster" id="button1">
<xp:eventHandler event="onclick" submit="true"
refreshMode="complete">
<xp:this.action><![CDATA[#{javascript:view.postScript("Toaster('"+getComponent("toaster").getClientId(facesContext)+"', 'toaster message!')");}]]></xp:this.action>
</xp:eventHandler>
</xp:button>
</xp:view>
But when I click button,the toaster can't show.Why?
And I need a way to show the toaster called by ssjs function.
Thanks a lot.
As #Frantisek Kossuth said, you're re-submitting the page. When your refreshMode is complete it will re-render the page based on the server values. If you want to make client side changes and reflect them in your UI, you can't do a refresh of complete. Doing a partial refresh of the page on an element that won't change (like button1) should update your UI without rebuilding the page.
Similarly, why do you need this to be done through SSJS? Why not just use Client actions in your button activation? If this isn't part of a larger function call that you're not including, simply change the event code from Server to Client and call the Toaster function that way. This would also allow you to not have to submit anything, leaving submit="false" in your eventHandler to avoid unnecessary functionality.