validator update number formulario form ejemplo combo ajax validation jsf primefaces

ajax - update - primefaces input text number validation



¿Cómo puedo rellenar un campo de texto usando PrimeFaces AJAX después de que ocurran los errores de validación? (3)

Como explicó BalusC, también puede agregar un oyente reutilizable que limpia todos los valores de entrada, por ejemplo:

public class CleanLocalValuesListener implements ActionListener { @Override public void processAction(ActionEvent actionEvent) throws AbortProcessingException { FacesContext context = FacesContext.getCurrentInstance(); UIViewRoot viewRoot = context.getViewRoot(); List<UIComponent> children = viewRoot.getChildren(); resetInputValues(children); } private void resetInputValues(List<UIComponent> children) { for (UIComponent component : children) { if (component.getChildCount() > 0) { resetInputValues(component.getChildren()); } else { if (component instanceof EditableValueHolder) { EditableValueHolder input = (EditableValueHolder) component; input.resetValue(); } } } } }

Y úsala cada vez que necesites limpiar tus valores locales:

<f:actionListener type="com.cacib.bean.CleanLocalValuesListener"/>

Tengo un formulario en una vista que realiza un procesamiento parcial ajax para autocompletar y localización gmap. Mi bean de respaldo instancia un objeto de entidad "Dirección" y es a este objeto que se hace referencia a las entradas del formulario:

@ManagedBean(name="mybean") @SessionScoped public class Mybean implements Serializable { private Address address; private String fullAddress; private String center = "0,0"; .... public mybean() { address = new Address(); } ... public void handleAddressChange() { String c = ""; c = (address.getAddressLine1() != null) { c += address.getAddressLine1(); } c = (address.getAddressLine2() != null) { c += ", " + address.getAddressLine2(); } c = (address.getCity() != null) { c += ", " + address.getCity(); } c = (address.getState() != null) { c += ", " + address.getState(); } fullAddress = c; addMessage(new FacesMessage(FacesMessage.SEVERITY_INFO, "Full Address", fullAddress)); try { geocodeAddress(fullAddress); } catch (MalformedURLException ex) { Logger.getLogger(Mybean.class.getName()).log(Level.SEVERE, null, ex); } catch (UnsupportedEncodingException ex) { Logger.getLogger(Mybean.class.getName()).log(Level.SEVERE, null, ex); } catch (IOException ex) { Logger.getLogger(Mybean.class.getName()).log(Level.SEVERE, null, ex); } catch (ParserConfigurationException ex) { Logger.getLogger(Mybean.class.getName()).log(Level.SEVERE, null, ex); } catch (SAXException ex) { Logger.getLogger(Mybean.class.getName()).log(Level.SEVERE, null, ex); } catch (XPathExpressionException ex) { Logger.getLogger(Mybean.class.getName()).log(Level.SEVERE, null, ex); } } private void geocodeAddress(String address) throws MalformedURLException, UnsupportedEncodingException, IOException, ParserConfigurationException, SAXException, XPathExpressionException { // prepare a URL to the geocoder address = Normalizer.normalize(address, Normalizer.Form.NFD); address = address.replaceAll("[^//p{ASCII}]", ""); URL url = new URL(GEOCODER_REQUEST_PREFIX_FOR_XML + "?address=" + URLEncoder.encode(address, "UTF-8") + "&sensor=false"); // prepare an HTTP connection to the geocoder HttpURLConnection conn = (HttpURLConnection) url.openConnection(); Document geocoderResultDocument = null; try { // open the connection and get results as InputSource. conn.connect(); InputSource geocoderResultInputSource = new InputSource(conn.getInputStream()); // read result and parse into XML Document geocoderResultDocument = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(geocoderResultInputSource); } finally { conn.disconnect(); } // prepare XPath XPath xpath = XPathFactory.newInstance().newXPath(); // extract the result NodeList resultNodeList = null; // c) extract the coordinates of the first result resultNodeList = (NodeList) xpath.evaluate( "/GeocodeResponse/result[1]/geometry/location/*", geocoderResultDocument, XPathConstants.NODESET); String lat = ""; String lng = ""; for (int i = 0; i < resultNodeList.getLength(); ++i) { Node node = resultNodeList.item(i); if ("lat".equals(node.getNodeName())) { lat = node.getTextContent(); } if ("lng".equals(node.getNodeName())) { lng = node.getTextContent(); } } center = lat + "," + lng; }

Las solicitudes de autocompletado y ajax del mapa funcionan bien antes de procesar todo el formulario en el envío. Si la validación falla, ajax aún funciona bien, excepto el campo fullAddress que no se puede actualizar en la vista, incluso cuando su valor se establece correctamente en el bean de respaldo después de la solicitud de ajax.

<h:outputLabel for="address1" value="#{label.addressLine1}"/> <p:inputText required="true" id="address1" value="#{mybean.address.addressLine1}"> <p:ajax update="latLng,fullAddress" listener="#{mybean.handleAddressChange}" process="@this"/> </p:inputText> <p:message for="address1"/> <h:outputLabel for="address2" value="#{label.addressLine2}"/> <p:inputText id="address2" value="#{mybean.address.addressLine2}" label="#{label.addressLine2}"> <f:validateBean disabled="#{true}" /> <p:ajax update="latLng,fullAddress" listener="#{mybean.handleAddressChange}" process="address1,@this"/> </p:inputText> <p:message for="address2"/> <h:outputLabel for="city" value="#{label.city}"/> <p:inputText required="true" id="city" value="#{mybean.address.city}" label="#{label.city}"> <p:ajax update="latLng,fullAddress" listener="#{mybean.handleAddressChange}" process="address1,address2,@this"/> </p:inputText> <p:message for="city"/> <h:outputLabel for="state" value="#{label.state}"/> <p:autoComplete id="state" value="#{mybean.address.state}" completeMethod="#{mybean.completeState}" selectListener="#{mybean.handleStateSelect}" onSelectUpdate="latLng,fullAddress,growl" required="true"> <p:ajax process="address1,address2,city,@this"/> </p:autoComplete> <p:message for="state"/> <h:outputLabel for="fullAddress" value="#{label.fullAddress}"/> <p:inputText id="fullAddress" value="#{mybean.fullAddress}" style="width: 300px;" label="#{label.fullAddress}"/> <p:commandButton value="#{label.locate}" process="@this,fullAddress" update="growl,latLng" actionListener="#{mybean.findOnMap}" id="findOnMap"/> <p:gmap id="latLng" center="#{mybean.center}" zoom="18" type="ROADMAP" style="width:600px;height:400px;margin-bottom:10px;" model="#{mybean.mapModel}" onPointClick="handlePointClick(event);" pointSelectListener="#{mybean.onPointSelect}" onPointSelectUpdate="growl" draggable="true" markerDragListener="#{mybean.onMarkerDrag}" onMarkerDragUpdate="growl" widgetVar="map"/> <p:commandButton id="register" value="#{label.register}" action="#{mybean.register}" ajax="false"/>

Si actualizo la página, los mensajes de error de validación desaparecen y el ajax completa el campo fullAddress como se esperaba.

Otro comportamiento extraño ocurre también durante la validación: he desactivado la validación de beans para un campo de formulario, como se ve en el código. Esto funciona bien hasta que se encuentren otros errores de validación, entonces, si vuelvo a enviar el formulario, ¡JSF realiza la validación de frijol para este campo!

Supongo que me falta algo durante el estado de validación, pero no puedo entender cuál es el problema. ¿Alguien sabe cómo depurar el ciclo de vida JSF? ¿Algunas ideas?


Dentro de su etiqueta <p:ajax/> , agregue un atributo resetValues="true" para decirle a la vista que busque datos de nuevo, de esta manera debería ser capaz de solucionar su problema.


La causa del problema puede ser entendida considerando los siguientes hechos:

  • Cuando la validación JSF tiene éxito para un componente de entrada particular durante la fase de validación, el valor presentado se establece en null y el valor validado se establece como el valor local del componente de entrada.

  • Cuando la validación de JSF falla para un componente de entrada particular durante la fase de validación, el valor enviado se mantiene en el componente de entrada.

  • Cuando al menos un componente de entrada no es válido después de la fase de validaciones, entonces JSF no actualizará los valores del modelo para ninguno de los componentes de entrada. JSF procederá directamente a procesar la fase de respuesta.

  • Cuando JSF renderiza los componentes de entrada, primero probará si el valor presentado no es null y luego lo mostrará; de lo contrario, si el valor local no es null y luego lo muestra, de lo contrario mostrará el valor del modelo.

  • Siempre que interactúe con la misma vista JSF, se trata del mismo estado de componente.

Por lo tanto, cuando la validación ha fallado para un envío de formulario en particular y necesita actualizar los valores de los campos de entrada mediante una acción ajax diferente o incluso una forma ajax diferente (por ejemplo, rellenando un campo dependiendo de una selección desplegable o el resultado de algún formulario de diálogo modal, etc.), entonces básicamente necesita reiniciar los componentes de entrada de destino para que JSF muestre el valor del modelo que se editó durante la acción de invocación. De lo contrario, JSF seguirá mostrando su valor local tal como estaba durante la falla de validación y los mantendrá en un estado invalidado.

Una de las formas en su caso particular es recolectar manualmente todas las ID de los componentes de entrada que serán actualizados / re-renderizados por PartialViewContext#getRenderIds() y luego reiniciar manualmente su estado y los valores enviados por EditableValueHolder#resetValue() .

FacesContext facesContext = FacesContext.getCurrentInstance(); PartialViewContext partialViewContext = facesContext.getPartialViewContext(); Collection<String> renderIds = partialViewContext.getRenderIds(); for (String renderId : renderIds) { UIComponent component = viewRoot.findComponent(renderId); EditableValueHolder input = (EditableValueHolder) component; input.resetValue(); }

Puede hacer esto dentro del método listener handleAddressChange() , o dentro de una implementación de ActionListener reutilizable que adjunta como <f:actionListener> al componente de entrada que está llamando al método listener handleAddressChange() .

Volviendo al problema concreto, me imagino que esto es un descuido en la especificación JSF2. Tendría mucho más sentido para nosotros, los desarrolladores JSF, cuando la especificación JSF exige lo siguiente:

  • Cuando JSF necesita actualizar / volver a procesar un componente de entrada mediante una solicitud ajax, y ese componente de entrada no se incluye en el proceso / ejecución de la solicitud ajax, entonces JSF debe restablecer el valor del componente de entrada.

Esto se informó como JSF issue 1060 y se implementó una solución completa y reutilizable en la biblioteca de ResetInputAjaxActionListener como ResetInputAjaxActionListener (código fuente here y demostración de demostración here ).

Actualización 1: desde la versión 3.4, PrimeFaces se basó en esta idea y también introdujo una solución completa y reutilizable en el sabor de <p:resetInput> .

Actualización 2: desde la versión 4.0, el <p:ajax> obtuvo un nuevo atributo booleano resetValues que también debería resolver este tipo de problema sin la necesidad de una etiqueta adicional.

Actualización 3: JSF 2.2 introdujo <f:ajax resetValues> , siguiendo la misma idea que <p:ajax resetValues> . La solución ahora es parte de la API JSF estándar.