当前位置: 首页 > 面试题库 >

发生验证错误后,如何使用PrimeFaces AJAX填充文本字段?

严曜文
2023-03-14
问题内容

我在视图中有一个表单,该表单执行自动完成和gmap本地化的ajax部分处理。我的支持bean实例化了一个实体对象“
Address”,并向该对象引用了表单的输入:

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

自动补全和map
ajax请求可以正常工作,然后再提交时处理整个表单。如果验证失败,则即使无法在视图中更新fullAddress字段(即使在ajax请求之后在备用bean上正确设置了其值),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"/>

如果刷新页面,验证错误消息就会消失,并且ajax会按预期完成fullAddress字段。

验证期间还会发生另一种怪异的行为:如代码所示,我已禁用了表单字段的bean验证。在发现其他验证错误之前,此工作正常进行,然后,如果我重新提交表单,JSF将对该字段进行bean验证!

我想我在验证状态中遗漏了一些东西,但是我无法弄清楚它出了什么问题。有谁知道如何调试JSF生命周期?有任何想法吗?


问题答案:

考虑以下事实可以理解问题的原因:

  • 当在验证阶段针对特定输入组件的JSF验证成功时,则将提交的值设置为null,并将验证的值设置为输入组件的本地值。

  • 如果在验证阶段对特定输入组件的JSF验证失败,则提交的值将保留在输入组件中。

  • 在验证阶段之后,如果至少一个输入组件无效,那么JSF将不会更新任何输入组件的模型值。JSF将直接进入渲染响应阶段。

  • 当JSF渲染输入组件时,它将首先测试提交的值是否不是null,然后显示它;如果本地值不是null,然后显示它,否则它将显示模型值。

  • 只要您与相同的JSF视图进行交互,就可以处理相同的组件状态。

因此,当针对特定表单提交的验证失败时,您碰巧需要通过不同的ajax动作甚至不同的ajax表单(例如,根据下拉选择或某些结果填充字段)来更新输入字段的值。模态对话框形式等),那么您基本上需要重置目标输入组件,以使JSF可以显示在调用动作期间编辑的模型值。否则,JSF仍将显示其本地值,如在验证失败期间一样,并将其保持在无效状态。

您的特定情况下
,方法之一是手动收集要由更新/重新呈现的输入组件的所有ID,PartialViewContext#getRenderIds()然后由手动重置其状态和提交的值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()侦听器方法内部ActionListener,也可以<f:actionListener>在与调用handleAddressChange()侦听器方法的输入组件相连的可重用实现中进行此操作。

回到具体问题,我想这是对JSF2规范的疏忽。当JSF规范强制执行以下命令时,对我们JSF开发人员而言将更加有意义:

  • 当JSF需要通过ajax请求更新/重新呈现输入组件,并且该输入组件不包含在ajax请求的处理/执行中时,则JSF应该重置输入组件的值。

这已被报告为JSF问题1060和一个完整的,可重复使用的解决方案已在已实施OmniFaces库作为ResetInputAjaxActionListener(源代码在这里,展示演示在这里)。

更新1:
从3.4版开始,PrimeFaces基于此思想还引入了一个完整且可重复使用的解决方案<p:resetInput>

更新2:
从版本4.0开始,<p:ajax>有一个新的布尔属性resetValues,该属性也可以解决此类问题,而无需其他标签。

更新3: 引入了JSF 2.2 <f:ajax resetValues>,遵循与相同的想法<p:ajax resetValues>。该解决方案现在是标准JSF API的一部分。



 类似资料:
  • 我使用的是带有AppCompatEditText的TextInputLayout,它为AppCompatEditText提供了一个基于xml形状的自定义背景。每当我设置一些错误,错误线从布局的开始。有没有办法给那个错误行做填充。

  • 我们为RSA签名创建了调试公钥和私钥,并生成sha256哈希并保存在cp1.bin中。然后我们尝试验证签名,但总是失败。如果我们不使用pss填充,验证命令将通过。任何人都可以帮忙评论吗? 谢谢 C:\Project\pkc C:\Project\pkc

  • 驱动程序的fire中有一个bug。使用chrome驱动解决了这个问题。 代码 我试过联系搜索。clear()和click()方法,但输入仍然为空。代码来自https://web.whatsapp.com/登录后会出现一个名为“搜索”或“开始新聊天”的字段。我想在该字段中输入文本。

  • 因此,我试图在我的JavaFX应用程序中创建一个自定义节点,它从扩展而来,因此可以自己进行渲染。我一开始只是试着画一个文本“Hello world”在画布上,但可惜它没有出现,即使我可以通过鼠标事件处理程序确认应用程序中是否存在自定义节点。 简而言之,如果我将这个的一个新实例添加到一个

  • 你知道我如何使用FireStore填充文档上的引用字段吗?

  • 问题内容: 如何从文本文件填充? 问题答案: 非常模糊的问题。您是说要每行输入一个吗?如果是这样,则要使用BufferedReader之类的东西,请读取所有行,并将它们保存为String数组。创建一个新的JComboBox传入该String构造函数。