简体   繁体   English

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

[英]JSF 1.1: How to display component label instead of clientId in validation messages?

I'm writing a custom component to show validation messages in popups, it works well but I have still a problem: I would like to display component labels instead of cliendIds in validation messages. 我正在编写一个自定义组件来显示弹出窗口中的验证消息,它运行良好但我还有一个问题:我想在验证消息中显示组件标签而不是cliendIds。

I already looked at this question and similars: 我已经看过这个问题和类似的了:

Removing the component Id from validation message when using message bundle in JSF 在JSF中使用消息包时从验证消息中删除组件Id

but I'm using jsf 1.1 and I get a compilation error if I try to set a value for attribute label or requiredMessage . 但我正在使用jsf 1.1,如果我尝试为属性labelrequiredMessage设置一个值,我会收到编译错误。 So I tried assigning a label to the component using an <outputLabel /> : 所以我尝试使用<outputLabel />为组件分配标签:

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

with no effect. 没有效果。

Is there a simple way in jsf 1.1 to make label appear in validation messages instead of client id? 在jsf 1.1中是否有一种简单的方法可以使标签出现在验证消息中而不是客户端ID? If not, how could I, given a component, retrieve its related label component to do the work in Java code? 如果没有,在给定组件的情况下,我如何检索其相关的标签组件以在Java代码中完成工作?

Thanks. 谢谢。

I found a solution which does not require to modify existing JSF code. 我找到了一个不需要修改现有JSF代码的解决方案。

  1. make my component renderer extend Tomahawk's HtmlMessagesRenderer , which is capable of use a component label instead of its id 使我的组件渲染器扩展Tomahawk的HtmlMessagesRenderer ,它能够使用组件标签而不是其id
  2. use HtmlMessagesRenderer#findInputId to retrieve the Id of the component with messages 使用HtmlMessagesRenderer#findInputId检索带有消息的组件的Id
  3. use HtmlMessagesRenderer#findInputLabel to retrieve the Label of the component with messages 使用HtmlMessagesRenderer#findInputLabel检索带有消息的组件的Label
  4. replace component Id with component Label in the message. 在消息中用组件标签替换组件Id。

Below is an excerpt from the code of my component Render encodeBegin method, in which I made two separated loops, the first for component messages the other for global messages. 下面是我的组件Render encodeBegin方法的代码的摘录,其中我做了两个独立的循环,第一个用于组件消息,另一个用于全局消息。

Note that FacesContext#getClientIdsWithMessages returns also null , which is considered the clientId for global messages. 请注意, FacesContext#getClientIdsWithMessages也返回null ,它被视为全局消息的clientId。

Note that, because the component itself manages to retrieve and use the component label, if exists; 请注意,因为组件本身设法检索并使用组件标签,如果存在; this solution it only need to place a <myCustomMessages /> tag in JSF page code. 这个解决方案只需要在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 />");
        }
// ...

Here is a reference to HtmlMessagesRenderer#findInputLabel source code, to take a look on how it works. 这里是对HtmlMessagesRenderer#findInputLabel源代码的引用,以了解它是如何工作的。

I think of one solution. 我想到了一个解决方案。 You can add new PhaseListener which will look for PhaseEvent with PhaseId = RENDER_RESPONSE and inside it's beforePhase method write something like this: 您可以添加新的PhaseListener ,它将查找具有PhaseId = RENDER_RESPONSE PhaseEvent ,并在其之前的beforePhase方法中写入如下内容:

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();
    } 
  }

...

}

Now in Render Phase which comes after this method all messages in FacesContext.getCurrentInstance().getMessages() will have changed clientId with label value and because Validation Phase is before Render Phase all validation will be passed or failed - no more messages will come from validation. 现在在此方法之后的Render阶段, FacesContext.getCurrentInstance().getMessages()所有消息都将更改带有标签值的clientId ,并且因为验证阶段在渲染阶段之前所有验证都将通过或失败 - 不再有消息来自验证。

The only problem is when you use in for attribute of HtmlOutputLabel instance the same id twice or more because in different forms can be same ids used for components. 唯一的问题是,当你在使用for属性HtmlOutputLabel因为在不同形式的实例相同的ID两次以上可用于组件的编号相同。 Then You can in for attr inside <h:outputLabel> use more sophisticated id, for example clue it with form id or something like this. 然后你可以在for <h:outputLabel>里面<h:outputLabel>使用更复杂的id,例如用表格id或类似的东西来表达它。


I hope this will help. 我希望这将有所帮助。

  1. bind your output label . 绑定输出标签。
  2. bind your error message. 绑定您的错误消息。
  3. now set value of your output label from java. 现在从java设置输出标签的值。
  4. check validation if validation failed get your output label value and set it to in your error message variable. 检查验证,如果验证失败,请获取输出标签值并将其设置为错误消息变量。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM