El renderizador personalizado no activa el detector para el evento ajax
-
26-12-2019 - |
Pregunta
Hola de nuevo JSF Gurus.
Estoy buscando ayuda sobre renderizadores personalizados y controladores ajax.Nuestro proyecto no puede utilizar los renderizadores normales de botones de opción y casillas de verificación, ya que se representan en tablas y esto va en contra de nuestros estándares corporativos.Tampoco podemos utilizar bibliotecas de componentes de terceros.
Tenemos un renderizador personalizado que hemos usado en algunos proyectos y ahora funciona bien.Sin embargo, en este proyecto en particular, necesito representar el controlador de clic ajax para algunos botones de opción.Siguiendo el consejo de la respuesta a esta pregunta He añadido la llamada a RenderKitUtils.renderSelectOnclick();
y aunque parece renderizar el mismo javaScript que el renderizador estándar y, de hecho, el panel de red de Firebug muestra la solicitud saliente, mi oyente de cambio de valor no se activa y estoy buscando ayuda para saber por qué (se activa perfectamente con el renderizador estándar renderizador)
Algún código:En primer lugar, la creación del controlador ajax (el botón de opción se crea mediante programación)
AjaxBehavior valueChangeAction = (AjaxBehavior) FacesUtils.getApplication().createBehavior(AjaxBehavior.BEHAVIOR_ID);
valueChangeAction.addAjaxBehaviorListener(new ProbeQuestionListener(currentQuestion, "probeDiv" + questionNumber,
probeDiv));
selectUI.addClientBehavior("valueChange", valueChangeAction);
valueChangeAction.setRender(Collections.singletonList("probeDiv" + questionNumber));
Con el renderizador estándar, esto produce:
<table id="answer_1" class="marginLeft1a">
<tbody>
<tr>
<td>
<input id="answer_1:0" type="radio" onclick="mojarra.ab(this,event,'valueChange',0,'probeDiv2')" value="0" name="answer_1">
<label for="answer_1:0"> Yes</label>
</td><td>
<input id="answer_1:1" type="radio" onclick="mojarra.ab(this,event,'valueChange',0,'probeDiv2')" value="1" name="answer_1">
<label for="answer_1:1"> No</label>
</td>
</tr>
y todo está bien.La solicitud ajax se publica y el oyente del lado del servidor se activa.
Las partes relevantes de mi renderizador personalizado:
public void encodeEnd(final FacesContext p_context, final UIComponent p_component) throws IOException {
/* set up convenience fields */
context = p_context;
component = p_component;
componentId = component.getClientId(context);
// rendering divs etc omitted.
while (iterator.hasNext()) {
final UIComponent childComponent = iterator.next();
// irrelevant code omited
if ("javax.faces.SelectItem".equals(childComponent.getFamily())) {
renderSelectItem(idIndex, childComponent);
idIndex++;
}
}
}
private void renderSelectItem(final int p_idIndex, final UIComponent p_childComponent) throws IOException {
// unrelated code omitted
writer.startElement("input", component);
writer.writeAttribute("type", getInputType(), null);
writer.writeAttribute("id", p_childComponentId, "id");
writer.writeAttribute("name", componentId, "clientId");
RenderKitUtils.renderSelectOnclick(context, component, false);
writer.endElement("input");
// unrelated code omitted
}
Actualizar:El método de decodificación en la clase base para mi renderizador es:
@Override
public void decode(final FacesContext p_context, final UIComponent p_component) {
final Map<String, String> valuesMap = p_context.getExternalContext().getRequestParameterMap();
final UIInput input = (UIInput) p_component; // NOSONAR
final Object value = valuesMap.get(input.getClientId(p_context));
// System.out.println("radio button decode: found " + value);
input.setSubmittedValue(value);
}
Esto se representa como:
<span class="marginLeft1a" id="answer_1">
<label for="answer_1:0">
<input type="radio" value="0" onclick="mojarra.ab(this,event,'valueChange',0,'probeDiv2')" name="answer_1" id="answer_1:0">
Yes
</label>
<label for="answer_1:1">
<input type="radio" value="1" onclick="mojarra.ab(this,event,'valueChange',0,'probeDiv2')" name="answer_1" id="answer_1:1">
No
</label>
</span>
Como puede ver, el controlador de clics de JavaScript es idéntico, al igual que los valores de los atributos de nombre e identificación de las etiquetas de entrada.Aunque esto desencadena la solicitud de Ajax, el oyente no se activa en el lado del servidor como ocurre con la primera versión.
Algunas ideas ?
Solución
El decode()
método de la Renderer
(o UIComponent
si no hay un renderizador) se supone que decodifica la solicitud ajax en función de javax.faces.behavior.event
solicitar parámetro y disparador ClientBehavior#decode()
sobre los comportamientos coincidentes del cliente.
Básicamente, la lógica es la siguiente:
Map<String, List<ClientBehavior>> behaviors = ((ClientBehaviorHolder) component).getClientBehaviors();
if (!behaviors.isEmpty()) {
Map<String, String> params = context.getExternalContext().getRequestParameterMap();
String behaviorEvent = params.get("javax.faces.behavior.event");
if (behaviorEvent != null) {
List<ClientBehavior> behaviorsForEvent = behaviors.get(behaviorEvent);
if (behaviorsForEvent != null && !behaviorsForEvent.isEmpty()) {
String behaviorSource = params.get("javax.faces.source");
if (isBehaviorSource(context, behaviorSource, component.getClientId())) {
for (ClientBehavior behavior: behaviorsForEvent) {
behavior.decode(context, component);
}
}
}
}
}
Dado el hecho de que estás usando RenderKitUtils
y la salida HTML generada contiene mojarra.ab()
, Asumiré que estás usando Mojarra.En ese caso, quizás sea más prudente ampliar su renderizador personalizado desde RadioRenderer
en cambio, que tiene toda esta lógica ya implementada en HtmlBasicRenderer
y SelectManyCheckboxListRenderer
para que no tengas que reinventar la rueda.