Situation
I'm using a custom error XPage, based highly off of the XSnippet from Tony McGuckin. It works rather well but I would like for the browser to execute a client-side JavaScript block (or load and run a JS file from a given URL). If I navigate directly to the custom error XPage, it loads correctly, but given the nature of how it loads on redirect from a SSJS runtime error, it seems to load any attempts at loading a script block in the head tag, inside the body tag. I've attempted passing through a JS script tag in the body (shown in the code below), attempted using the xp:headTag inside xp:resources, and attempted via an xp:script tag in xp:resources.
Browser's Perspective
From the browser's perspective, after encountering a runtime error during an event that invokes SSJS during a partial refresh, the xhr being invoked returns with a 500 and sets the content into the body tag (screen shot).
When viewing the response contents, the entire custom error XPage is there, including the <script type="text/javascript">console.log("hello world");<script>
. This does not seem to trigger or put anything out to the JS console of the browser. What is visible via the JS console is some garbage from dojo complaining about getting back an XHR with response code of 500 (my dojoConfig is set to isDebug: true
via xsp.client.script.dojo.djConfig in XSP Properties).
Question
Is there a way to get a client-side JS script tag to load and execute in the browser after an error 500 which occurs during the loading of a custom error XPage?
Here's the code for my Error page. To reproduce my results, invoke an SSJS action resulting in a runtime error (such as the ErrorOnClick XPage included in the OpenLog Logger for XPages project from Paul Withers) with a partial refresh event.
Error.xsp (set as the error page in XSP Properties)
<?xml version="1.0" encoding="UTF-8"?>
<xp:view
xmlns:xp="http://www.ibm.com/xsp/core"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.ibm.com/xsp/core xsdxp://localhost/xsp~core.xsd"
pageTitle="${javascript:database.getTitle() + ' | Error'}">
<style
type="text/css"><![CDATA[
body {
background-color: lightblue;
}
form {
width: 1000px !important;
margin: 0 auto !important;
background-color: white !important;
margin-top: 2rem !important;
padding: 0.5rem !important;
height: auto;
}
.xspTextLabel {
font-weight: bold !important;
}
]]></style>
<img
class="logo-simple"
src="//placehold.it/124x32" />
<xp:panel>
<xp:br></xp:br>
<xp:br></xp:br>
<xp:label
style="font-weight:bold;font-size:12pt"
value="An Unexpected Error Has Occurred:">
</xp:label>
<xp:br></xp:br>
<xp:br></xp:br>
<xp:label
value="Error:"></xp:label>
<xp:br />
<xp:text
escape="false">
<xp:this.value><![CDATA[#{javascript:if( !!requestScope.error ){
var output = (requestScope.error.toString() || null)+"<br /><br />";
if(requestScope.error instanceof com.ibm.xsp.exception.XSPExceptionInfo){
var codeSnippet = requestScope.error.getErrorText();
var control = requestScope.error.getErrorComponentId();
var cause = requestScope.error.getCause();
output += "In the control : " + control + "<br /><br />";
if(cause instanceof com.ibm.jscript.InterpretException){
var errorLine = cause.getErrorLine();
var errorColumn = cause.getErrorCol();
output += "At line " + errorLine;
output += ", column " + errorColumn + " of:<br />";
}else{
output += "In the script:<br />";
}
if( @Contains(codeSnippet,"#{javascript:") ){
var snipAr = codeSnippet.split("#{javascript:");
var tmpSnip = snipAr[1];
var nwSnip = tmpSnip.substring(0, tmpSnip.length - 1);
output += "#{javascript:<br /><pre>"+nwSnip+"</pre>}"
}else{
output += codeSnippet;
}
}
return output;
}else{
return "";
}}]]></xp:this.value>
</xp:text>
<xp:br></xp:br>
<xp:br></xp:br>
<xp:label
value="Stack Trace:"></xp:label>
<xp:br />
<xp:text
escape="false"
style="font-size:10pt">
<xp:this.value><![CDATA[#{javascript:if( !!requestScope.error ){
var stackTrace = "";
var trace = (requestScope.error.getStackTrace() || null);
if(trace != null){
for(var i = 0; i < trace.length; i++){
stackTrace += trace[i] + "<br/>";
}
return "<pre>"+stackTrace+"</pre>";
}else{
return "nothing";
}
}else{
return "";
}}]]></xp:this.value>
</xp:text>
</xp:panel>
<script
type="text/javscript">
<![CDATA[console.log("Hello world...");]]>
</script>
</xp:view>
For what it's worth: I didn't find anything explicitly on this subject via a search of Google or StackOverflow.
UPDATE 1: This was a case of either caffeine deprivation or just not seeing the forest through the trees. It helps to not use a CDATA block in your HTML code. The lazy developer in me tried copying and pasting between an xp:script block and the HTML <script> block, preserving it. Now for the public shaming of buying Marky beer in Atlanta.
UPDATE 2: Marky's beverage of choice may be in peril. While I seem to have had issues with copying a CDATA tag in, the issue remains. In my efforts to produce a simplified page with a button to error out (loosely based on the above mentioned XPage from the OpenLog Logger for XPages ErrorOnClick.xsp), I mistakenly took out a part of what was causing my issues in the first place, the partial refresh. When I do a full refresh, no issue, but when I do a partial, it doesn't load. I'm enclosing a sample page to trigger an error, with two buttons; one to induce a full, the other a partial. SO, with a full refresh, I get an alert of "hello world...", with the partial, no dice.
MakeSomeError.xsp
<?xml version="1.0" encoding="UTF-8"?>
<xp:view
xmlns:xp="http://www.ibm.com/xsp/core">
<xp:panel
id="somePanel">
<xp:button
value="Failing Partial"
id="button1">
<xp:eventHandler
event="onclick"
submit="true"
refreshMode="partial"
refreshId="somePanel">
<xp:this.action><![CDATA[#{javascript:var a:NotesDateTime = null;
viewScope.myStuff = a.toJavaDate().toDateString();}]]></xp:this.action>
</xp:eventHandler>
</xp:button>
<xp:button
value="Failing Full"
id="button2">
<xp:eventHandler
event="onclick"
submit="true"
refreshMode="complete">
<xp:this.action><![CDATA[#{javascript:var a:NotesDateTime = null;
viewScope.myStuff = a.toJavaDate().toDateString();}]]></xp:this.action>
</xp:eventHandler>
</xp:button>
<xp:br />
<xp:text
value="#{viewScope.myStuff}" />
</xp:panel>
</xp:view>
UPDATE 3: Okay. Sven's second answer has me very close, but for some reason I can't extrapolate just enough to get what I want to happen to occur. I'm including a GIF below of my results. The only thing different I would like to have happen is for my Error.xsp (custom error XPage) to continue loading after I encounter the error (it seems like I'll need to change the beforeRenderResponse block to an afterRenderResponse script perchance?). I want to append the script, not replace the Error.xsp loading. Basically, I'm trying to run a script after the error XPage is loaded (there's a helper JS file I'm trying to load into my custom error XPage, CSS is loading fine, just not the JS lib). I would love to:
- get this working
- share what it is (it's kind of cool, if I do say so myself)