I visit this website to learn to use postscript in xpages. I follow the example to run the code and it runs property.
Then I begin to try other way. I use a computed field that use #DbLookup and a button that will use view.postScript to open xpage in a new window. When #DbLookup returns the value, that value will be the parameter in view.postScipt.
When I run the code, it returns error said Java method 'postScript(Array)' on java class 'com.ibm.xsp.component.UIViewRootEx2' not found
I get confuse about the error since it mentions it is java error and I don't have java code in the xpage.
I think the reason I get the error is the computed field returns more than one value.
However if I hard code values, it run fine
var myparam = "Test,Test2,Test3";
Or
var myparam = "Test"+"Test2"+"Test3";
Please find the code for your review please.(It includes the code that cause error, my attempts and the error message)
<?xml version="1.0" encoding="UTF-8"?>
<xp:view xmlns:xp="http://www.ibm.com/xsp/core">
<xp:br></xp:br>
<xp:text escape="true" id="computedField1">
<xp:this.value><![CDATA[#{javascript:
//use #DbLookup to find value, that value will be the parameter in postScript
var value = #DbLookup(#DbName(), "myView", keyword, 3);
return value;
}]]></xp:this.value>
</xp:text>
<xp:br></xp:br>
<xp:br></xp:br>
<xp:button value="Label" id="button1">
<xp:eventHandler event="onclick" submit="true"
refreshMode="complete">
<xp:this.action><![CDATA[#{javascript:
var myparam = getComponent("computedField1").getValue();//does not work
//var myparam = "Test,Test2,Test3"; //works
//var myparam = "Test"+"Test2"+"Test3"; //works
//var myparam = "Test"; //(original) works
var myurl = #LeftBack(context.getUrl(),"/") + "/testpostscript.xsp?id="+myparam;
//error occurs in this code
//Java method 'postScript(Array)' on java class 'com.ibm.xsp.component.UIViewRootEx2' not found
view.postScript("window.open('" + myurl + "')");}]]></xp:this.action>
</xp:eventHandler></xp:button>
<xp:br></xp:br>
<xp:br></xp:br>
</xp:view>
I read this post and it seems different to my question.
When I search "postScript(Array) xpages" on the Internet, I don't find much information about it.
So my question is how to let view.postScript runs when the parameter contains multiple values?
Grateful for your advice please. Thank you.
You are right that the result of your #DbLookup returns more than one value (an array). That's why you see the underlying Java error that postScript(Array) is an unknown method. The postScript method expects a String as input.
I will suggest that you use #Implode() to join the result from the #DbLookup:
<xp:button value="Label" id="button1">
<xp:eventHandler event="onclick" submit="true" refreshMode="complete">
<xp:this.action>
<![CDATA[#{javascript:
var myparam = #Implode(getComponent("computedField1").getValue(); ",")
var myurl = #LeftBack(context.getUrl(),"/") + "/testpostscript.xsp?id="+myparam;
view.postScript("window.open('" + myurl + "')");}]]>
</xp:this.action>
</xp:eventHandler>
</xp:button>
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 want to be able to prompt the user for a comment and then be able to send this variable comment in an email. However, I'm unable to pass that variable from CSJS to SSJS. My document is in read mode.
Here is a sample button code where I can't seem to pass my comment.
Does anyone know how to do this?
Thanks in advance :)
<xp:panel readonly="false">
<xp:inputHidden id="inputHidden1" value="#{viewScope.tester}">
</xp:inputHidden>
</xp:panel>
<xp:button value="Reject" id="button7" style="margin-right:5.0px"
save="false">
<xp:eventHandler event="onclick" submit="true" refreshMode="complete">
<xp:this.action>
<xp:actionGroup>
<xp:executeScript>
<xp:this.script><![CDATA[#{javascript:var doc:NotesDocument = currentDocument.getDocument();
doc.replaceItemValue("status", "0");
doc.save();
database.updateFTIndex(false);
var comment = viewScope.tester; /* HOW DO I GET COMMENT FROM CSJS TO HERE */
var ndoc = database.createDocument();
ndoc.appendItemValue("from", "tome#somewhere.com");
ndoc.appendItemValue("SendTo", "someone#somewhere.com);
ndoc.appendItemValue("subject", "My Subject");
var rti:NotesRichTextItem = ndoc.createRichTextItem("Body");
rti.appendText("Reason:" + comment + "\n\n");
ndoc.send()
}]]></xp:this.script>
</xp:executeScript>
<xp:openPage name="/mainpage.xsp"></xp:openPage>
</xp:actionGroup>
</xp:this.action>
<xp:this.script>
<xp:executeClientScript>
<xp:this.script><![CDATA[
var comment = XSP.prompt("Please enter a comment:");
XSP.getElementById("#{id:inputHidden1}").value = comment;
/*
XSP.partialRefreshGet("#{id:computedField1}",
{
params:{"para1":"1", "para2":"2"}
});
*/
]]></xp:this.script>
</xp:executeClientScript>
</xp:this.script>
</xp:eventHandler>
</xp:button>
XSP.prompt will use the default browser prompt window. You will have limited functionality from it and, in my opinion, not a great user experience. I would recommend having your reject button open an XPages Extension Library dialog with an Edit Box to enter the comment. Bind the Edit Box to a requestScope variable - you won't need the value after the dialog closes, so you would need it at a higher scope level. In the "OK" button of the dialog run your SSJS and use the SSJS hide() method of the Dialog component to close the dialog, adding a parameter for a component on the page if you need to partially refresh it.
Don't bind that field to your document (in read mode). Bind it to viewScope or requestScope variable.
Also, look here for inspiration.
To accomplish this in the past, I have used CSS to hide the input fields and buttons. The client-side script is defined separately from the button actions.
<xp:text escape="false"><xp:this.value><![CDATA[
<script>
function promptBox(){
// ... do whatever to get the data input into
// XSP.getElementById("#{id:inputText1}").value
// OR if using JQuery $('.tester).val()
// then click hidden Save button
$('.reject').click()
// OR
XSP.getElementById("#{id:button1}").click()
}
</script>
]]></xp:this.value></xp:text>
<!-- hide the input field with CSS -->
<xp:inputText
id="inputText1"
value="#{viewScope.tester}"
defaultValue="#{viewScope.tester}"
styleClass="tester hidden">
</xp:inputText>
<!-- hide the button with CSS -->
<xp:button
value="Reject"
styleClass="reject hidden"
id="button1">
<xp:eventHandler event="onclick" submit="true" refreshMode="complete">
<xp:this.action>
<xp:actionGroup>
<xp:executeScript>
<xp:this.script><![CDATA[#{javascript:var doc:NotesDocument = currentDocument.getDocument();
doc.replaceItemValue("status", "0");
doc.save();
database.updateFTIndex(false);
var comment = viewScope.tester; /* scoped variable already set */
var ndoc = database.createDocument();
ndoc.appendItemValue("from", "tome#somewhere.com");
ndoc.appendItemValue("SendTo", "someone#somewhere.com);
ndoc.appendItemValue("subject", "My Subject");
var rti:NotesRichTextItem = ndoc.createRichTextItem("Body");
rti.appendText("Reason:" + comment + "\n\n");
ndoc.send()
}]]></xp:this.script>
</xp:executeScript>
<xp:openPage name="/mainpage.xsp"></xp:openPage>
</xp:actionGroup>
</xp:button>
<!-- use link for button - style it with CSS -->
Reject
I do not like having both client-side and server-side scripts bound to the same button, the execution order of the code is not easily managed. There are other ways to combine these client side fields and server side scripts, but this is a simple demonstration.
I would like to be able to add a user as a member into an excisting group (Newsletter).
With the code I have so far a new group is created with all the users and also the new member.
So , instead of inserting the user the code makes a new group (Newsletter) with the users.
<xp:this.data>
<xp:dominoDocument var="usergroup"
databaseName="contacts/webusers.nsf" formName="(46Group)"
action="editDocument" computeWithForm="onsave">
</xp:dominoDocument>
</xp:this.data>
<xp:br></xp:br>
<xp:inputText id="fullname" value="#{viewScope.fullname}"></xp:inputText>
<xp:br></xp:br>
<xp:br></xp:br>
<xp:br></xp:br>
<xp:button value="Add new user" id="button1">
<xp:eventHandler event="onclick" submit="true" refreshMode="complete">
<xp:this.action>
<xp:actionGroup>
<xp:executeScript>
<xp:this.script><![CDATA[#{javascript:var who = #DbLookup("contacts/webusers.nsf","($VIMGroups)","Newsletter",3);
var newone = getComponent("fullname").getValue();
who = who + ", "+newone;
usergroup.replaceItemValue("Members",who);
usergroup.replaceItemValue("ListName","Newsletter");
usergroup.save();}]]></xp:this.script>
</xp:executeScript>
<xp:openPage name="/login.xsp"></xp:openPage>
</xp:actionGroup>
</xp:this.action>
</xp:eventHandler>
</xp:button>
EDIT
New code :
var ndb = session.getDatabase("","contacts/webusers.nsf")
var d = ndb.getView("($VIMGroups)").getDocumentByKey("Newsletter")
var newArr = new Array(d.getItemValue("Members"));
newArr =newArr + ", another user";
d.replaceItemValue("Members",newArr);
d.save(true,false);
As per suggestion of Thomas I changed my code.
The user is added to the members, but it is added x times. and x is the number of excisting members. I also tried to add the member with push, but this doesn't work. How can I add a member in the array in a proper way ?
The "usergroup" data source is not connected to the group document so the data source creates a new document when you load the page
You need to connect your data source to the group document, once you have that set you do not need to do the dblookup, you can just add the new user to the document data source field and save it.
You should be able to connect your data source doing something like this, (not tested)
var ndb = session.getDatabase("","contacts/webusers.nsf")
var d = ndb.getView("($VIMGroups)").getDocumentByKey("Newsletter")
return d.getUniversalID()
Here is some working code you can use in your button, I had to remove the data source as I was not allowed to save it due to security,
<?xml version="1.0" encoding="UTF-8"?>
<xp:view xmlns:xp="http://www.ibm.com/xsp/core">
<xp:inputText id="inputText1"></xp:inputText>
<xp:button value="Add new user" id="button1">
<xp:eventHandler event="onclick" submit="true" refreshMode="complete">
<xp:this.action><![CDATA[#{javascript:var ndb = sessionAsSignerWithFullAccess.getDatabase("server","names.nsf")
var d = ndb.getView("($VIMGroups)").getDocumentByKey("Newsletter")
var members = d.getItemValue("Members")
members.add(getComponent("inputText1").getValue())
d.replaceItemValue("Members",members)
d.save()}]]></xp:this.action>
</xp:eventHandler>
</xp:button>
</xp:view>
I write a xpages.
detail: There are two combobox A,B. I use #Dbcolumn on combobox A to get option data from notesview and I will throw the choice I get from A to get second data for B.
the problem is: it work well on my localserver, but get no result on the server.
I'll be very appreciate for any suggestion, thanks you!!
code is on server side as follow:
var fd_AppChoice:com.ibm.xsp.component.xp.XspSelectOneMenu = getComponent("fd_AppChoice");
var AppChoice=#Trim(fd_AppChoice.getValue());
var temp=new Array();
temp=#DbLookup("","(A)",AppChoice,2);
return temp;
That code doesn't look right - you don't have the server. And defining a variable doesn't fix its data type, so var temp=new Array(); is irrelevant. I also would rather bind the fd_AppChoice to a scope variable e.g. viewScope.appChoice, then your code get easier. Try this:
var appChoice = #Trim(viewScope.appChoice); // Use getComponent.getValue if you have to
var server = #DbName();
// if different server or nsf have = ["myserver","mydb.nsf"] or [#DbName()[0],"my.nsf"]
var result = #DbLookup(server,"(A)",appChoice,2);
return result || ["Sorry nothing here"]
That should work
I can't quite confirm this: in my case it works like a charm on my test server (didn't even try locally). Here's my code:
comboBox #1 reads its values from a categorized view of the same database:
<xp:comboBox id="comboBox1" value="#{viewScope.combo1}">
<xp:selectItems>
<xp:this.value>
<![CDATA[#{javascript:#DbColumn(#DbName(), "myView", 1);}]]>
</xp:this.value>
</xp:selectItems>
<xp:eventHandler event="onchange" submit="true" refreshMode="partial" refreshId="panelC2">
</xp:eventHandler>
</xp:comboBox>
Observe that the combo's onchange event performs a partial update on a panel which is a container for comboBox #2 (could it be that this is missing in your case?)
To get through with this, here's the remainder: combo#2 gets its values array using a #DbLookup which is filtered by the value selected in combo#1, which now is stored in a viewScope variable (how couldn't I agree with Stephan here: using a scope-var make things much easier!):
<xp:panel id="panelC2">
<xp:comboBox id="comboBox2" value="#{viewScope.combo2}">
<xp:selectItems>
<xp:this.value>
<![CDATA[#{javascript:#DbLookup(#DbName(), "myView", viewScope.combo1, 5);}]]>
</xp:this.value>
</xp:selectItems>
</xp:comboBox>
</xp:panel>
I have an XPage which has just broken due to (what should have been) a small change.
If I run (where document1 is NotesXSPDocument datasource) :
document1.replaceItem("ItemName", false); //or true, or any variable/formula that results in a boolean
followed by:
document1.getDocument(true);
I end up with an error
[TypeError] Exception occurred calling method NotesXspDocument.getDocument(boolean) null
This is on a new document (so not saved yet, which I've not tested, but might make a difference), hence I can't just set the field on the underlying doc.
It doesn't seem to be an artifact of anything else in the page, as a basic test page confirms it. It doesn't happen with anything (I've found) other than a boolean.
Any advice, other than just change the data type? I guess that's what I'll end up doing (along with a bug report) but I'd like to know I'm not missing something first.
Thanks
------------Test XSP code------------
<?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="Test" />
</xp:this.data><xp:span style="font-weight:bold">
Button 1 code:</xp:span><xp:br></xp:br>document1.replaceItemValue("Test",false);
<xp:br></xp:br>var doc:NotesDocument = document1.getDocument(true);
<xp:br></xp:br>print(doc.toString());<xp:br></xp:br>
<xp:button value="Test 1" id="button1">
<xp:eventHandler event="onclick" submit="true"
refreshMode="complete">
<xp:this.action><![CDATA[#{javascript:document1.replaceItemValue("Test",false);
var doc:NotesDocument = document1.getDocument(true);
print(doc.toString());}]]></xp:this.action>
</xp:eventHandler></xp:button>
<xp:br></xp:br>
<xp:br></xp:br>
<xp:br></xp:br>
<xp:br></xp:br><xp:span style="font-weight:bold">
Button</xp:span><xp:span style="font-weight:bold"> 2</xp:span><xp:span style="font-weight:bold"> Code:</xp:span><xp:span style="font-weight:bold"></xp:span> <xp:br></xp:br>document1.replaceItemValue("Test","Test);<xp:br></xp:br>var doc:NotesDocument = document1.getDocument(true);
<xp:br></xp:br>print(doc.toString());<xp:br></xp:br><xp:button value="Test 2" id="button2">
<xp:eventHandler event="onclick" submit="true"
refreshMode="complete">
<xp:this.action><![CDATA[#{javascript:document1.replaceItemValue("Test","Test");
var doc:NotesDocument = document1.getDocument(true);
print(doc.toString());}]]></xp:this.action>
</xp:eventHandler></xp:button></xp:view>
If you have a look in the stack trace you will see the following:
....
NotesException: Unknown or unsupported object type in Vector
lotus.domino.local.Document.NreplaceItemValue(Native Method)
lotus.domino.local.Document.replaceItemValue(Unknown Source)
com.ibm.xsp.model.domino.wrapped.DominoDocument.applyChangesToDoc(DominoDocument.java:1698)
com.ibm.xsp.model.domino.wrapped.DominoDocument.applyChanges(DominoDocument.java:1649)
com.ibm.xsp.model.domino.wrapped.DominoDocument.getDocument(DominoDocument.java:544)
com.ibm.xsp.script.WrapperDominoEx$fct_DominoDocument.call(WrapperDominoEx.java:254)
....
The problem is that you use the replaceItemValue method with an unsupported data type.
Here is a list of all allowed data types:
String Text
Integer Number
Double Number
DateTime Date-time item
java.util.Vector with String, Integer, Double, or DateTime elements Multi-value text, number, or date-time item
Item Same data type as the Item
The error is raised in the moment the backend document is synchronized with the datasource document. That's why it fails while calling document1.getDocument(true).
Hope this helps
Sven
EDIT:
Why are you setting the value to false?