1

我正在编写一个自定义组件来在弹出窗口中显示验证消息,它运行良好,但我仍然有一个问题:我想在验证消息中显示组件标签而不是 cliendIds。

我已经看过这个问题和类似的东西:

在 JSF 中使用消息包时从验证消息中删除组件 ID

但我使用的是 jsf 1.1,如果我尝试为属性labelrequiredMessage. 因此,我尝试使用以下方法为组件分配标签<outputLabel />

<h:outputLabel for="phone" value="Phone number" />
<h:inputText id="phone" required="true" value="#{backingBean.phoneNumber}" />

没有效果。

jsf 1.1 中是否有一种简单的方法可以使标签出现在验证消息中而不是客户端 ID 中?如果没有,给定一个组件,我如何检索其相关的标签组件以在 Java 代码中完成工作?

谢谢。

4

3 回答 3

2

我找到了一个不需要修改现有 JSF 代码的解决方案。

  1. 让我的组件渲染器扩展 Tomahawk's HtmlMessagesRenderer,它能够使用组件标签而不是它的 id
  2. 用于HtmlMessagesRenderer#findInputId通过消息检索组件的 Id
  3. 用于HtmlMessagesRenderer#findInputLabel检索带有消息的组件的标签
  4. 将消息中的组件 ID 替换为组件标签。

下面是我的组件 Render 方法的代码摘录encodeBegin,其中我制作了两个单独的循环,第一个用于组件消息,另一个用于全局消息。

请注意,FacesContext#getClientIdsWithMessages还返回null,它被认为是全局消息的 clientId。

请注意,因为组件本身设法检索和使用组件标签(如果存在);这个解决方案只需要<myCustomMessages />在 JSF 页面代码中放置一个标签。

public void encodeBegin(FacesContext context, UIComponent component) throws IOException {
// ....

// replace id with label in messages for components
        Iterator<String> clientIdsWithMessages=context.getClientIdsWithMessages();
        while(clientIdsWithMessages.hasNext()){
            String clientIdWithMessages=clientIdsWithMessages.next();
            if(clientIdWithMessages!=null){
                Iterator<FacesMessage> componentMessages=context.getMessages(clientIdWithMessages);
                while(componentMessages.hasNext()){
                    String stringMessage=componentMessages.next().getDetail();
                    try{
                        String inputId =findInputId(context, clientIdWithMessages);
                        String inputLabel = findInputLabel(context, clientIdWithMessages);
                        if(inputId!=null && inputLabel!=null)
                            stringMessage=stringMessage.replace(inputId, inputLabel);
                    } catch(Exception e){
                        // do nothing in this catch, just let the message be rendered with the componentId instead of a label                       }
                    msgBuilder.append(stringMessage);
                    msgBuilder.append("<br />");
                }
            }
        }


        // process global messages
        Iterator<FacesMessage> globalMessages=context.getMessages(null);
        while(globalMessages.hasNext()){
            FacesMessage message=globalMessages.next();
            msgBuilder.append(message.getDetail());
            msgBuilder.append("<br />");
        }
// ...

这是对HtmlMessagesRenderer#findInputLabel源代码的参考,以了解它是如何工作的。

于 2013-01-25T09:10:32.960 回答
1

我想到了一种解决方案。您可以添加新PhaseListener的,它将在 beforePhase 方法中查找PhaseEventPhaseId = RENDER_RESPONSE在其内部编写如下内容:

public class MyPhaseListener implements PhaseListener {

  private void findKeyValuePairInTree(UIComponent root, 
                                      Map<String, String> map) {
    if (root instanceof HtmlOutputLabel) {
      map.put(((HtmlOutputLabel) root).getFor(), 
                       ((HtmlOutputLabel) root).getValue());
    }
    if (root.getChilder() != null && !root.getChildren().isEmpty()) {
      for (UIComponent child : root.getChilder()) {
        findKeyValuePairInTree(child, map);
      }
    }

  private HashMap<String, String> idToLabelMap= new HashMap<String,String>();

  public void beforePhase(PhaseEvent event) {
    if (event.getPhaseId().equals(PhaseId.RENDER_RESPONSE) {

      findKeyValuePairInTree(FacesContext.getCurrentInstance().getViewRoot(),
                             idToLabelMap);
      // after above function finished execution in idToLabelMap will be all 
      // pairs of component ID used in standard FacesMessage as key
      // and HtmlOutputLabel value attr used as value
     
      Set<String> keySet = idToLabelMap.keySet();
      for(String key : keySet) {
        Iterator messages = FacesContext.getCurrentInstance().getMessages(key);
        while (iterator.hasNext) {
          FacesMessage msg = (FacesMessage) iterator.next();

          // now replace all occurences of clientId with label value from map

          msg.setSummary(msg.getSummary().replaceAll(key, idToLabelMap.get(key)));
          msg.setDetails(msg.getDetails().replaceAll(key, idToLabelMap.get(key)));
        }
      }
      FacesContext.getCurrentInstance().getMessages();
    } 
  }

...

}

现在在此方法之后的渲染阶段,所有消息FacesContext.getCurrentInstance().getMessages()都将clientId随着标签值的变化而变化,并且因为验证阶段在渲染阶段之前,所有验证都将通过或失败 - 不再有来自验证的消息。

唯一的问题是,当您在实例的for属性中使用HtmlOutputLabel相同的 id 两次或更多时,因为在不同的形式中可以使用相同的 id 用于组件。然后你可以在forattr 里面<h:outputLabel>使用更复杂的 id,例如用表单 id 或类似的东西来提示它。

于 2013-01-17T19:33:34.907 回答
0
  1. 绑定你的输出标签。
  2. 绑定你的错误信息。
  3. 现在从java设置你的输出标签的值。
  4. 如果验证失败,请检查验证获取您的输出标签值并将其设置为您的错误消息变量。
于 2013-01-17T09:47:59.787 回答