我正在编写一个自定义组件来显示弹出窗口中的验证消息,它工作得很好,但我仍然有一个问题:我想在验证消息中显示组件标签而不是cliendIds。
我已经看了这个问题和类似的问题:
在JSF中使用消息包时从验证消息中删除组件Id
,但我使用jsf 1.1,我得到一个编译错误,如果我试图设置属性label
或requiredMessage
的值。因此,我尝试使用<outputLabel />
为组件分配标签:
<h:outputLabel for="phone" value="Phone number" />
<h:inputText id="phone" required="true" value="#{backingBean.phoneNumber}" />
无效
是否有一个简单的方法在jsf 1.1使标签出现在验证消息,而不是客户端id?如果没有,我如何在给定组件的情况下检索其相关的标签组件以在Java代码中完成工作?
谢谢。
我找到了一个不需要修改现有JSF代码的解决方案。
- 让我的组件渲染器扩展战斧的
HtmlMessagesRenderer
,它能够使用组件标签而不是它的id - 使用
HtmlMessagesRenderer#findInputId
检索消息 组件的Id - 使用
HtmlMessagesRenderer#findInputLabel
检索消息 组件的Label - 将消息中的组件Id替换为组件标签
下面是我的组件Render encodeBegin
方法的代码摘录,其中我制作了两个分开的循环,第一个用于组件消息,另一个用于全局消息。
注意,FacesContext#getClientIdsWithMessages
也返回null
,它被认为是全局消息的clientId。
注意,因为组件本身能够检索和使用组件标签,所以if存在;这个解决方案只需要在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
源代码的参考,看看它是如何工作的
我想到一个解决办法。你可以添加新的PhaseListener
,它将查找PhaseEvent
和PhaseId = RENDER_RESPONSE
,并在它的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();
}
}
...
}
现在在这个方法之后的渲染阶段,FacesContext.getCurrentInstance().getMessages()
中的所有消息都将改变clientId
的标签值,因为验证阶段在渲染阶段之前,所有验证将通过或失败-没有更多的消息将来自验证。
唯一的问题是当您在HtmlOutputLabel
实例的for
属性中使用相同的id两次或更多次时,因为在不同的形式中可以为组件使用相同的id。然后你可以在for
attr中在<h:outputLabel>
中使用更复杂的id,例如用form id或类似的东西提示它
- 绑定你的输出标签。 绑定错误信息。
- 现在设置java输出标签的值。
- 检查验证,如果验证失败,获取输出标签值并将其设置为错误消息变量。