JSF 1.1:如何在验证消息中显示组件标签而不是clientId?

时间:2013-01-17 08:42:47

标签: jsf

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

我已经看过这个问题和类似的事了:

Removing the component Id from validation message when using message bundle in JSF

但我正在使用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代码中完成工作?

感谢。

3 个答案:

答案 0 :(得分:2)

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

  1. 让我的组件渲染器扩展Tomahawk的HtmlMessagesRenderer,它能够使用组件标签而不是其ID
  2. 使用HtmlMessagesRenderer#findInputId检索包含消息的组件的ID
  3. 使用HtmlMessagesRenderer#findInputLabel检索带有消息的组件的标签
  4. 将消息中的组件标签替换为组件标识。
  5. 下面摘录了我的组件Render encodeBegin方法的代码,其中我创建了两个独立的循环,第一个用于组件消息,另一个用于全局消息。

    请注意,FacesContext#getClientIdsWithMessages也会返回null,它被视为全局消息的clientId。

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

    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源代码的引用,以了解其工作原理。

答案 1 :(得分:1)

我想到了一个解决方案。您可以添加新的PhaseListener,其中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。然后你可以在for <h:outputLabel>里面使用更复杂的id,例如用表格id或类似的东西来表达它。


我希望这会有所帮助。

答案 2 :(得分:0)

  1. 绑定您的输出标签。
  2. 绑定您的错误消息。
  3. 现在设置java输出标签的值。
  4. 检查验证,如果验证失败,请获取输出标签值并将其设置为错误消息变量。