我正在编写一个自定义组件来显示弹出窗口中的验证消息,它运行良好但我仍然有问题:我想在验证消息中显示组件标签而不是cliendIds。
我已经看过这个问题和类似的事了:
Removing the component Id from validation message when using message bundle in JSF
但我正在使用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代码中完成工作?
感谢。
答案 0 :(得分:2)
我找到了一个不需要修改现有JSF代码的解决方案。
HtmlMessagesRenderer
,它能够使用组件标签而不是其ID HtmlMessagesRenderer#findInputId
检索包含消息的组件的ID HtmlMessagesRenderer#findInputLabel
检索带有消息的组件的标签下面摘录了我的组件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
,其中PhaseEvent
将PhaseId = 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)