superior - quitar el javascript que bloquea la visualización de contenido html
XPage de error personalizado: posibilidad de que el navegador cargue y ejecute el enlace o el script de JS después de cargarse en caso de error (4)
Situación
Estoy usando un XPage de error personalizado, basado altamente en el XSnippet de Tony McGuckin . Funciona bastante bien, pero me gustaría que el navegador ejecute un bloque de JavaScript del lado del cliente (o cargue y ejecute un archivo JS desde una URL determinada). Si navego directamente al XPage de error personalizado, se carga correctamente, pero dada la naturaleza de cómo se carga al redirigir desde un error de tiempo de ejecución SSJS, parece cargar cualquier intento de cargar un bloque de script en la etiqueta head, dentro de la etiqueta body . Intenté pasar una etiqueta de script JS en el cuerpo (que se muestra en el código a continuación), intenté usar el xp: headTag dentro de xp: resources e intenté a través de una etiqueta xp: script en xp: resources .
Perspectiva del navegador
Desde la perspectiva del navegador, después de encontrar un error de tiempo de ejecución durante un evento que invoca SSJS durante una actualización parcial, el xhr que se invoca retorna con un 500 y establece el contenido en la etiqueta del cuerpo (captura de pantalla).
Al ver los contenidos de la respuesta, está toda la página XPage de error personalizado, incluido <script type="text/javascript">console.log("hello world");<script>
. Esto no parece activar o poner nada a la consola JS del navegador. Lo que se ve a través de la consola JS es algo de basura del dojo que se queja de recuperar un XHR con un código de respuesta de 500 (mi dojoConfig está configurado en isDebug: true
través de xsp.client.script.dojo.djConfig en Propiedades XSP).
Pregunta
¿Hay alguna forma de obtener una etiqueta de script JS del lado del cliente para cargar y ejecutar en el navegador después de un error 500 que se produce durante la carga de un XPage de error personalizado?
Aquí está el código para mi página de Error. Para reproducir mis resultados, invoque una acción SSJS que resulte en un error de tiempo de ejecución (como ErrorOnClick XPage incluido en el proyecto OpenLog Logger para páginas XP de Paul Withers) con un evento de actualización parcial.Error.xsp (configurado como la página de error en Propiedades XSP)
<?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>
Por lo que vale: no encontré nada explícitamente sobre este tema a través de una búsqueda de Google o StackOverflow.
ACTUALIZACIÓN 1: Este fue un caso de carencia de cafeína o simplemente no ver el bosque a través de los árboles. Ayuda a no usar un bloque CDATA en su código HTML. El programador perezoso en mí intentó copiar y pegar entre un bloque de script xp: y el bloque HTML <script> , conservándolo. Ahora, por la vergüenza pública de comprar cerveza Marky en Atlanta.ACTUALIZACIÓN 2: la bebida preferida de Marky puede estar en peligro. Si bien parece que tuve problemas para copiar una etiqueta CDATA, el problema persiste. En mis esfuerzos por producir una página simplificada con un botón para el error de salida (basado libremente en la página XPage mencionada anteriormente de OpenLog Logger para XPages ErrorOnClick.xsp), tomé por error una parte de lo que estaba causando mis problemas, en primer lugar, la actualización parcial. Cuando hago una actualización completa, no hay problema, pero cuando hago un parcial, no se carga. Estoy adjuntando una página de muestra para activar un error, con dos botones; uno para inducir un lleno, el otro un parcial. ASÍ, con una actualización completa, recibo una alerta de "hola mundo ...", con el parcial, sin dados.
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>
ACTUALIZACIÓN 3: Bien. La segunda respuesta de Sven me tiene muy cerca, pero por alguna razón no puedo extrapolar lo suficiente para lograr lo que quiero que ocurra. Estoy incluyendo un GIF debajo de mis resultados. Lo único diferente que me gustaría que suceda es que mi Error.xsp (XPage de error personalizado) continúe cargando después de que encuentre el error (parece que tendré que cambiar el bloque beforeRenderResponse por una secuencia de comandos afterRenderResponse ?). Quiero agregar la secuencia de comandos, no reemplazar la carga Error.xsp. Básicamente, estoy intentando ejecutar un script después de cargar el error XPage (hay un archivo JS auxiliar que estoy tratando de cargar en mi XPage de error personalizado, CSS se carga bien, pero no la JS lib). Me encantaría:
- conseguir esto funcionando
- compartir lo que es (es genial, si lo digo yo mismo)
La razón de esto es el comportamiento es que esta es una característica de seguridad. Los navegadores no ejecutan bloques <script>
si se cargaron mediante Ajax. Pero hay una solución:
Primero, debe agregar un div para reemplazar su XPage de llamada:
<div id="errRefresh" />
Esto es solo un marcador de posición para la actualización parcial; de lo contrario, fallará.
Ahora, debe modificar su página de error para manejar los refrescos parciales. Para hacer esto, debe detectar si es una actualización o no, pero no puede usar la funcionalidad de compilación (está anulada en una página de error). Entonces tienes que hacer esto por tu cuenta:
var ex = facesContext.getExternalContext();
var pMap = com.ibm.xsp.util.TypedUtil().getRequestParameterMap(ex);
var refreshId = pMap.get("$$ajaxid");
Ahora debe establecer el código de estado de respuesta en 200, de lo contrario se llama al método de error del evento:
var resp:com.ibm.xsp.webapp.XspHttpServletResponse = facesContext.getExternalContext().getResponse();
resp.setStatus(200);
Luego, puede agregar su bloque CSJS que debe verse así:
<!-- XSP_UPDATE_SCRIPT_START -->
<script>
alert(''Hello World!'');
</script>
<!-- XSP_UPDATE_SCRIPT_END -->
Cuando se procesa la renovación parial, se reemplaza el elemento DOM actualizado, es por eso que tenemos que volver a enviar el marcado HTML con la respuesta y sobrescribir el X-XspRefreshId para forzar el reemplazo de nuestro elemento de error en su lugar:
resp.setHeader(''X-XspRefreshId'', ''errRefresh'' );
Por último, pero no por ello menos importante, tenemos que omitir el ciclo de vida de JSF:
facesContext.responseComplete();
Eso es.
Aquí está el código completo para el evento beforeRenderResponse de la página de error:
<xp:this.beforeRenderResponse>
<![CDATA[#{javascript:
var ex = facesContext.getExternalContext();
var pMap = com.ibm.xsp.util.TypedUtil().getRequestParameterMap(ex);
var refreshId = pMap.get("$$ajaxid");
if( refreshId ){
var resp:com.ibm.xsp.webapp.XspHttpServletResponse = ex.getResponse();
var writer:java.io.PrintWriter = resp.getWriter();
writer.write( "<!-- XSP_UPDATE_SCRIPT_START -->/n" );
writer.write( "<script>/n");
writer.write( "alert(''Hello World!'' );/n" );
writer.write( "</script>/n");
writer.write( "<!-- XSP_UPDATE_SCRIPT_END -->/n" );
writer.write( "<div id=/"errRefresh/" />/n");
resp.setStatus(200);
resp.setHeader(''X-XspRefreshId'', ''errRefresh'' );
facesContext.responseComplete();
}
}]]>
</xp:this.beforeRenderResponse>
Tenga en cuenta que esto podría generar problemas de seguridad.
Marky tiene otra buena idea: secuestrar la respuesta.
Esto podría verse así:
<script>
if( !dojo._xhr )
dojo._xhr = dojo.xhr;
var myHandler = function(){
var xhrObj = arguments[1].xhr;
var response = xhrObj.response;
var header = xhrObj.getResponseHeader(''X-XspRefreshId'');
if( header == "@error" ){
eval( response );
}else{
arguments[1]["error"]( arguments[0], arguments[1]);
}
}
dojo.xhr = function(){
try{
var args = arguments[1];
args["failOk"] = true;
args["error"] = myHandler;
arguments[1] = args;
}catch(e){}
dojo._xhr( arguments[0], arguments[1], arguments[2] );
}
</script>
El evento beforeRenderResponse tiene que modificarse así:
<xp:this.beforeRenderResponse>
<![CDATA[#{javascript:
var ex = facesContext.getExternalContext();
var pMap = com.ibm.xsp.util.TypedUtil().getRequestParameterMap(ex);
var refreshId = pMap.get("$$ajaxid");
if( refreshId ){
var resp:com.ibm.xsp.webapp.XspHttpServletResponse = facesContext.getExternalContext().getResponse();
var writer:java.io.PrintWriter = resp.getWriter();
writer.write( "alert(''Hello World!'' );/n" );
resp.setHeader(''X-XspRefreshId'', ''@error'' );
facesContext.responseComplete();
}
}]]>
</xp:this.beforeRenderResponse>
No use CDATA
en las etiquetas de script
.
<script>
alert(''hi Marky!'');
</script>
Funciona para mi.
OK, el último intento. Una muy interesante. En su Error.xsp, agregue la siguiente imagen:
<xp:text
escape="true"
id="executeOnAjax"
tagName="img">
<xp:this.attrs>
<xp:attr
name="src"
value="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw==">
</xp:attr>
<xp:attr
name="onload"
value="alert(''Hello World!'');this.parentNode.removeChild(this);">
</xp:attr>
</xp:this.attrs>
<xp:this.rendered>
<![CDATA[#{javascript:
var ex = facesContext.getExternalContext();
var pMap = com.ibm.xsp.util.TypedUtil().getRequestParameterMap(ex);
var refreshId = pMap.get("$$ajaxid");
refreshId?true:false;}]]>
</xp:this.rendered>
</xp:text>
si esto no se ajusta a sus requisitos, no entiendo lo que está tratando de lograr.