Unexpected Error: executing JavaScript/Reference error - xpages

I haven't made any changes to this database, and all of a sudden, I get an (500) error when loading various Xpages. On this one, when I switch to "Display the Xpage runtime error" on xpages tab of the database properties, it displays the errors below web.
If I remove the button that this Client side code is contained (it had worked perfectly to validate an Edit Box before), the error just moves to the next piece of JavaScript on the page and gives a similar error.
I've tried to "CLEAN" project. I've tried to Sign the entire database with the server id.
Any help would be much appreciated!
The runtime has encountered an unexpected error.
Error source
Page Name:/msr.xsp
Control Id: _id20
Exception
Error while executing JavaScript computed expression
Script interpreter error, line=3, col=8: [ReferenceError] 'XSP' not found
------the button control:
<xp:eventHandler event="onclick" submit="true" refreshMode="complete">
<xp:this.action>
<xp:actionGroup>
<xp:actionGroup>
<xp:modifyField
name="Status">
<xp:this.value><![CDATA[#{javascript:if (document1.isNewNote()) {
"Submitted to Project Officer";
}
else if (document1.Status == "Submitted to Project Officer"){
"Submitted to Supervisor";
}
else{
"Completed";
}}]]></xp:this.value>
</xp:modifyField>
<xp:saveDocument
var="document1">
</xp:saveDocument>
<xp:changeDocumentMode
mode="readOnly" var="document1">
</xp:changeDocumentMode>
</xp:actionGroup>
</xp:actionGroup>
</xp:this.action>
<xp:this.script>
<xp:executeClientScript>
<xp:this.script><![CDATA[#{javascript:
if(XSP.getElementById("#{id:ProjectTitle}").value == ""){
XSP.getElementById("#{id:ProjectTitle}").focus();
XSP.getElementById("#{id:ProjectTitle}").style.backgroundColor = "pink";
alert("Please enter a Project Title.");
return false;
}
else{
XSP.getElementById("#{id:ProjectTitle}").style.backgroundColor = "#ffe";
}
///NOT sole source
if(XSP.getElementById("#{id:RT}").innerHTML == "MSR"){
if(XSP.getElementById("#{id:SoleSource}").checked == false){
if(XSP.getElementById("#{id:SS_Name1}").value == ""){
XSP.getElementById("#{id:SS_Name1}").focus();
XSP.getElementById("#{id:SS_Name1}").style.backgroundColor = "pink";
alert("Please fill in the Name of Suggested Source 1.");
return false;
}
else{
XSP.getElementById("#{id:SS_Name1}").style.backgroundColor = "#ffe";
}
}
}
}]]></xp:this.script>
</xp:executeClientScript>
</xp:this.script></xp:eventHandler>
</xp:button>

The script property of your executeClientScript action looks like it is being computed. The source should look something like this:
<xp:executeClientScript>
<xp:this.script>
<![CDATA[if(XSP.getElementById("#{id:ProjectTitle}").value == ""){...
]]></xp:this.script>
</xp:executeClientScript>
Instead, you have this:
<xp:executeClientScript>
<xp:this.script>
<![CDATA[#{javascript:if(XSP.getElementById("#{id:ProjectTitle}").value == ""){...
}]]></xp:this.script>
</xp:executeClientScript>
That syntax implies that, instead of simply entering client-side JavaScript to execute, you're running server-side JavaScript to compute what the client-side JavaScript should be. Server-side JavaScript does not define a global XSP object like client-side JavaScript does, which is why you're getting a ReferenceError.
Remove #{javascript: from the beginning of your CDATA block and the final } from the end, and your code will execute in the context you intended.

Related

Xpages - Document refreshing with old values

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.

How to pass variable from CSJS to SSJS in read mode

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.

XPages appendDocLink on Anonymous User Error

I am attempting to replace my 'traditional Notes' User Name and Password Request form with one designed in XPages, mainly because the #SendMail Formula no longer works with Firefox or Chrome.
Anonymous users are to complete an XPage Form, hit a submit button which then sends an email to our support team, which is to include a Doc Link back to an internal Notes form of the same document, - I don't want to change this internal form as there is lots of lotusscript programming associated with it for processing.
I have the XPage marked as Available to Public Access as is the form associated with it. I also have ComputeWithForm set to both.
I can access the data document components e.g. append to the mail rtf body using doc.getItemValueString("field"), but I cannot do the same with appendDocLink it appears to fail due to access problems. If I set Anonymous to Manager access in the ACL the Doc link is appended.
<xp:button value="Submit" id="button1" style="margin-top:50.0px">
<xp:eventHandler event="onclick" submit="true" refreshMode="complete" immediate="false" save="false" id="eventHandler1">
<xp:this.action>
<xp:actionGroup>
<xp:saveDocument var="document1"></xp:saveDocument>
<xp:executeScript>
<xp:this.script><![CDATA[#{javascript: if(document1.isNewNote()){document1.save();}
var doc:NotesDocument = document1.getDocument(true);
var receiverEmail = "Support Requests";
var requestor = "xyz#company.co.uk";
db = sessionAsSignerWithFullAccess.getCurrentDatabase();
var memo = db.createDocument()
memo.appendItemValue("Form","Memo");
memo.appendItemValue("Principal","Company#NotesDomain");
memo.appendItemValue("From",requestor);
memo.appendItemValue("INetFrom",requestor);
memo.appendItemValue("DisplaySent",requestor);
memo.appendItemValue("SMTPOriginator",requestor);
memo.appendItemValue("Subject","NEW: Name & Password Request for "+getComponent("reqCompany1").getValue());
var rtitem:NotesRichTextItem = memo.createRichTextItem("Body");
rtitem.addNewLine();
rtitem.appendText("A new Name & Password Request has been logged by "+getComponent("reqFirstName1").getValue()+" " + getComponent("reqSecondName1").getValue()+" of "+ getComponent("reqCompany1").getValue());
rtitem.addNewLine();
rtitem.appendText("Click the Doc link to action it. ");
rtitem.appendDocLink(doc);
memo.send(receiverEmail);}]]></xp:this.script>
</xp:executeScript>
<xp:openPage name="/UserNandPSubmitted.xsp"></xp:openPage>
</xp:actionGroup>
</xp:this.action>
</xp:eventHandler>
</xp:button>
The following errors occur.
com.ibm.jscript.InterpretException: Script interpreter error, line=25, col=8: [TypeError] Exception occurred calling method NotesRichTextItem.appendDocLink(lotus.domino.local.Document)
null
NotesException: Notes error: You are not authorized to perform that operation
lotus.domino.local.RichTextItem.appendDocLink(Unknown Source)
lotus.domino.local.RichTextItem.appendDocLink(Unknown Source)
Any help would be greatly appreciated.
Make sure Anonymous user can access the default view of the database - so make it accessible to public access users. appendDocLink() method requires this view in order to append doc link to RT item.

How to perform multiple page updates behind a single button in XPages

This is a high level question but does have specific answers.
I have a requirement where I need to perform an action two times, and update the user with a message before trying the action a second time. I also want to update the user prior to the first action.
Here is the timeline/sequence of events:
User presses button
Display message "operation starting"
Perform operation
If successful, show "operation successful", if operation fails
show "operation failed - retrying"
Retry Operation
If successful, show "operation successful", if operation fails show "please try again later".
When I coded this in my java method, the only thing that displayed was the final message. I tried using Thread.sleep(1500); to give the message a chance to display, but ONLY the final message will ever be displayed.
I then tried using SSJS to accomplish this task, but the result is the same. I think I understand why this is happening.
My question is: Is it even possible to do multiple partial refreshes like this. What is the best approach that I can take to accomplish this requirement. What workarounds or hacks can anyone think of.
If you are wanting to just try twice, you could create an rpc control that does the functionality you want, then call it with a call back
var deferred = service.doSomethingCool();
deferred.addCallback(function(data) {
if (data.status == 'error') {
//set UI Label Fail
var insideDeferred = service.doSomethingCool();
insideDeferred.addCallback(function(data) {
if (data.status == 'error') {
//set UI Label Fail
}
else {
//set UI Label Success
}
}
}
}
This should work for you assuming you have an RPC control named service and method called "doSomethingCool"
I tried Mark's post for a progress bar once. http://linqed.eu/?p=174 Didn't get it to work but didn't go deep into the effort. The solution is probably in there.
What you want should be doable of course. I would think at the least you use CSJS to do the heavy lifting really. Have CSJS call SSJS probably via RPC or XAgent and that does all the work of first and secondary message.
I'm just light on the actual details of making that work. :)
Yes, it's possible. We can do back and forth client side and server side operations n number of times in a single button click.
Create a button with CSJS code for alert.
Write SSJS code for your operation.
Now the trick is depends on whether your operation is partial or complete refresh.
If partial refresh, use oncomplete / onfailure event in eventhandler to write CSJS code to perform button click operation. Use a hidden input field to store number of retry and to determine at what count the retry has to stop.
If full refresh, oncomplete event does not triggered. Write your CSJS code on clientload event. In this case, Create a hidden input field and bind it to a view scope variable. Use it to store the success or failure in SSJS code and retrieve the value in CSJS code to check and retry.
I have included a sample code with buttons however it can be implemented in many different ways.
<?xml version="1.0" encoding="UTF-8"?>
<xp:view xmlns:xp="http://www.ibm.com/xsp/core">
<xp:button value="Label" id="button1">
<xp:eventHandler event="onclick" submit="true" refreshMode="partial" refreshId="label1">
<xp:this.script><![CDATA[alert("operation starting");]]></xp:this.script>
<xp:this.action><![CDATA[#{javascript:viewScope.status = "first operation completed";
print(getComponent("fail").getValue());}]]></xp:this.action>
<xp:this.onComplete><![CDATA[alert("first operation completed. starting second");
document.getElementById("#{id:ihRetry}").value = "False";
document.getElementById("#{id:button2}").click();]]></xp:this.onComplete>
<xp:this.onError><![CDATA[if ( document.getElementById("#{id:ihRetry}").value == "False" ) {
alert("first operation failed. retry again");
document.getElementById("#{id:ihRetry}").value = "True";
document.getElementById("#{id:button1}").click();
} else {
alert("first operation failed again. starting second");
document.getElementById("#{id:ihRetry}").value = "False";
document.getElementById("#{id:button2}").click();
}]]></xp:this.onError>
</xp:eventHandler>
</xp:button>
<xp:button value="Label" id="button2" style="display:none">
<xp:eventHandler event="onclick" submit="true" refreshMode="partial" refreshId="label1">
<xp:this.action><![CDATA[#{javascript:viewScope.status = "second operation completed";}]]></xp:this.action>
<xp:this.onComplete><![CDATA[alert("second operation completed. starting third");
document.getElementById("#{id:ihRetry}").value = "False";
document.getElementById("#{id:button3}").click();]]></xp:this.onComplete>
<xp:this.onError><![CDATA[if ( document.getElementById("#{id:ihRetry}").value == "False" ) {
alert("second operation failed. retry again");
document.getElementById("#{id:ihRetry}").value = "True";
document.getElementById("#{id:button2}").click();
} else {
alert("second operation failed again. starting third");
document.getElementById("#{id:ihRetry}").value = "False";
document.getElementById("#{id:button3}").click();
}]]></xp:this.onError>
</xp:eventHandler>
</xp:button>
<xp:button value="Label" id="button3" style="display:none">
<xp:eventHandler event="onclick" submit="true" refreshMode="partial" refreshId="label1">
<xp:this.action><![CDATA[#{javascript:viewScope.status = "third operation completed";}]]></xp:this.action>
<xp:this.onComplete><![CDATA[alert("third operation completed.");
document.getElementById("#{id:ihRetry}").value = "False";]]></xp:this.onComplete>
<xp:this.onError><![CDATA[if ( document.getElementById("#{id:ihRetry}").value == "False" ) {
alert("third operation failed. retry again");
document.getElementById("#{id:ihRetry}").value = "True";
document.getElementById("#{id:button3}").click();
} else {
alert("third operation failed again. end");
document.getElementById("#{id:ihRetry}").value = "False";
}]]></xp:this.onError>
</xp:eventHandler>
</xp:button>
<xp:inputHidden id="ihRetry">
<xp:this.value><![CDATA["False"]]></xp:this.value>
</xp:inputHidden>
<xp:label value="#{viewScope.status}" id="label1"></xp:label>
</xp:view>

Exception thrown when deleting document in XPages via doc.remove() method

I added a Delete link in an xp:repeat to delete the document for the row, with "rowVar" the variable for the repeat to access thye document entries.
Here is the code for the Delete link in the repeat:
<xp:link escape="true" id="link2"
style="width:50.0px;display:inline-block" text="Delete">
<xp:eventHandler event="onclick" submit="true"
refreshMode="partial" refreshId="panelChemLog" immediate="true">
<xp:this.action><![CDATA[#{javascript:
var deleteUNID = rowVar.getUniversalID();
println(deleteUNID);
var doc:NotesDocument = database.getDocumentByUNID(deleteUNID);
println(doc.getCreated() );
doc.remove(true);
}]]></xp:this.action>
</xp:eventHandler>
</xp:link>
I included the println statements and I can see in my log that the deleteUNID and doc object is set, but the code throws this exception on the final doc.remove(true) statement:
Script interpreter error, line=5, col=5: [TypeError] Exception occurred calling method NotesDocument.remove(boolean) null
How do I code the Delete link to delete the document for the row in the repeat?
I've seen this error before several times. Just tested to be sure. If the user does not have deletion privilege, it will throw such a meaningless error.
If you look at the Java stack trace, you can confirm by the following at the bottom of the trace.
NotesException: Notes error: You are not authorized to perform that
operation
You might use database.queryAccessPrivileges(...) method to be sure within the code.

Resources