我如何填充使用PrimeFaces AJAX后出现验证错误的文本字段? [英] How can I populate a text field using PrimeFaces AJAX after validation errors occur?

查看:708
本文介绍了我如何填充使用PrimeFaces AJAX后出现验证错误的文本字段?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个观点这对于自动完成与GMAP本地化执行AJAX部分处理的形式。我支持bean实例化一个实体对象地址,并与当前对象窗体的输入被引用的:

I have a form in a view which performs ajax partial processing for autocompletion and gmap localization. My backing bean instantiates an entity object "Address" and is to this object that the form's inputs are referenced:

@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;
    }

自动完成和地图Ajax请求正常工作之前,我就处理提交整个表单。如果验证失败,阿贾克斯仍然工作正常,除了现场fullAddress这是无法更新的观点,即使它的值是正确的支持bean Ajax请求后进行设置。

Autocompletion and map ajax requests work fine before I process the whole form on submit. If validation fails, ajax still works ok except for the field fullAddress which is unable to update in the view, even when it's value is correctly set on the backing bean after the ajax request.

<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"/>

如果我刷新页面,验证错误消息消失,阿贾克斯完成预期fullAddress领域。

If I refresh the page, validation error messages disappear and the ajax completes fullAddress field as expected.

另外一个怪异的行为也发生在验证过程中:我有残疾豆验证表单域,因为看到在code。这项工作好了,直到找到其他验证错误,那么,如果我重新提交表单,JSF使Bean验证了这一领域!

Another weird behavior occurs also during validation: I have disabled bean validation for a form field, as seen on the code. This work alright until other validation errors are found, then, if I resubmit the form, JSF makes bean validation for this field!

我想我失去了一些东西在在验证状态,但我想不出有什么不妥的地方。有谁知道如何调试JSF生命周期?任何想法?

I guess I am missing something in during the validation state but I can't figure out what's wrong with it. Does anyone knows how to debug JSF life cycle? Any ideas?

推荐答案

该问题的原因可以通过考虑以下事实来理解:

The cause of the problem can be understood by considering the following facts:

  • 在JSF验证成功的在验证阶段特定的输入组件,然后提交的值设置为和验证的值设置为本地值输入组件。

  • When JSF validation succeeds for a particular input component during the validations phase, then the submitted value is set to null and the validated value is set as local value of the input component.

当JSF验证失败在验证阶段的特定输入组件,然后提交的值被保持在所述输入部件。

When JSF validation fails for a particular input component during the validations phase, then the submitted value is kept in the input component.

当至少一个输​​入组件是验证阶段之后无效,则JSF将不为任何输入分量的更新的模型值。 JSF将直接进入显示响应阶段。

When at least one input component is invalid after the validations phase, then JSF will not update the model values for any of the input components. JSF will directly proceed to render response phase.

在JSF呈现输入组件,那么它会如果提交的值是不是第一次测试,然后显示它,否则如果本地值不,然后显示它,否则它会显示模型的价值。

When JSF renders input components, then it will first test if the submitted value is not null and then display it, else if the local value is not null and then display it, else it will display the model value.

只要你使用相同的JSF视图进行交互,你在处理相同的组件状态。

As long as you're interacting with the same JSF view, you're dealing with the same component state.

所以,当验证失败的一个特殊形式提交,你碰巧需要由不同的AJAX动作,甚至是不同的Ajax表单更新输入字段的值(例如填充取决于下拉选择或场一些模态对话框形式等)的结果,那么你基本上需要重置目标输入组件,以获得JSF来显示这是调用动作中编辑的模型值。否则,JSF将仍然显示其当地的价值,因为它是在验证失败时,并让他们在一个无效的状态。

So, when the validation has failed for a particular form submit and you happen to need to update the values of input fields by a different ajax action or even a different ajax form (e.g. populating a field depending on a dropdown selection or the result of some modal dialog form, etc), then you basically need to reset the target input components in order to get JSF to display the model value which was edited during invoke action. Otherwise JSF will still display its local value as it was during the validation failure and keep them in an invalidated state.

一,在方式的您的特定情况下的是手动收集输入组件的所有ID这是要更新/重新呈现由<一个href="http://docs.oracle.com/javaee/6/api/javax/faces/context/PartialViewContext.html#getRenderIds%28%29"><$c$c>PartialViewContext#getRenderIds()然后手动<一个复位其状态和提交的值href="http://docs.oracle.com/javaee/6/api/javax/faces/component/EditableValueHolder.html#resetValue%28%29"><$c$c>EditableValueHolder#resetValue().

One of the ways in your particular case is to manually collect all IDs of input components which are to be updated/re-rendered by PartialViewContext#getRenderIds() and then manually reset its state and submitted values by 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();
}

您可以在 handleAddressChange()监听器方法内做到这一点,或可重复使用的内部<一个href="http://docs.oracle.com/javaee/6/api/javax/faces/event/ActionListener.html"><$c$c>ActionListener你作为附加<一实施href="http://docs.oracle.com/javaee/6/javaserverfaces/2.1/docs/vdldocs/facelets/f/actionListener.html"><$c$c><f:actionListener>到输入组件,它被调用 handleAddressChange()监听器方法。

You could do this inside the handleAddressChange() listener method, or inside an reuseable ActionListener implementation which you attach as <f:actionListener> to the input component which is calling the handleAddressChange() listener method.

让我们回到具体的问题,我会想象这是在JSF2规范的监督。它将使更多的意义对我们来说,JSF的开发人员,当JSF规范要求如下:

Coming back to the concrete problem, I'd imagine that this is an oversight in the JSF2 specification. It would make much more sense to us, JSF developers, when the JSF specification mandates the following:

  • 当的JSF需要更新/由一个AJAX请求重新渲染一个输入组件,并且该输入组件不包括在过程/ Ajax请求的执行,然后JSF应该重置输入组件的值。

这已被报告为 JSF发行1060 以及一个完整的,可重复使用的解决方案已经在<一实施HREF =htt​​p://omnifaces.org> OmniFaces 库<一个href="http://omnifaces.org/docs/javadoc/current/org/omnifaces/eventlistener/ResetInputAjaxActionListener.html"><$c$c>ResetInputAjaxActionListener (来源$ C ​​$ C <一个href="https://github.com/omnifaces/omnifaces/blob/master/src/main/java/org/omnifaces/eventlistener/ResetInputAjaxActionListener.java">here这里展示的演示 )。

This has been reported as JSF issue 1060 and a complete and reuseable solution has been implemented in the OmniFaces library as ResetInputAjaxActionListener (source code here and showcase demo here).

更新1:由于3.4版本,PrimeFaces已经基于这种想法还介绍了在<风味一个完整的,可重复使用的解决方案href="http://www.primefaces.org/showcase/ui/misc/resetInput.xhtml"><$c$c><p:resetInput>.

Update 1: Since version 3.4, PrimeFaces has based on this idea also introduced a complete and reusable solution in flavor of <p:resetInput>.

更新2:由于4.0版本中, &LT;电话号码:AJAX&GT; 得到了一个新的布尔属性 resetValues​​ 这也应该解决这样的问题不需要额外的标签。

Update 2: Since version 4.0, the <p:ajax> got a new boolean attribute resetValues which should also solve this kind of problem without the need for an additional tag.

更新3: JSF 2.2引入<一href="https://docs.oracle.com/javaee/7/javaserver-faces-2-2/vdldocs-facelets/f/ajax.html"><$c$c><f:ajax resetValues​​&GT; ,遵循相同的想法&LT;电话号码:AJAX resetValues​​&GT; 。该解决方案是目前标准的JSF API的一部分。

Update 3: JSF 2.2 introduced <f:ajax resetValues>, following the same idea as <p:ajax resetValues>. The solution is now part of standard JSF API.

这篇关于我如何填充使用PrimeFaces AJAX后出现验证错误的文本字段?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆