Hi all,

I found that org.jboss.seam.core.FacesMessages.addToControl() does not work 
with redirects, if the target control is not contained in the current page. 

Example: 

  | *  user enters input data into page1 and hits submit
  | *  validation finds that some input field on page2 is missing to complete 
the workflow, adds a message with FacesMessage.addToControl,
  | *  the action handler returns the target view id 
  | *  and a navigation rule in pages.xml redirects to page2
  | 

JSF doesn't seem to provide an appropriate hook to work around the issue. A 
PhaseListener didn't work, because the target component tree is only fully 
constructed during rendering. Querying the FacesContext viewRoot in 
before[RENDER_RESPONSE] returns a non-null value, however the component tree is 
not yet constructed at this stage (childrenList is null). 

Note that the described situation only applies to first-renders of a page. 
During a post-back, the tree would be available in before[RENDER_RESPONSE].

Because of the not-yet-constructed component tree, getClientId() in 
FacesMessages.addToControl(String, FacesMessage) returns null, and the message 
is lost.

The only solution that worked for me was saving unresolved client ids in 
FacesMessages.addToControl(String, FacesMessage), and implementing a custom 
ViewHandler that restored the faces messages for unresolved client ids. I 
extended org.jboss.seam.ui.facelet.SeamFaceletViewHandler, and restored 
unresolved client ids in getActionURL() - at this stage, the component tree is 
fully constructed.

Relevant parts of modified org.jboss.seam.ui.facelet.SeamFaceletViewHandler:

  | public class SeamFaceletViewHandler extends FaceletViewHandler {
  |         ...
  | 
  |     @Override
  |     public String getActionURL(FacesContext context, String viewId) {
  |             if (Contexts.isConversationContextActive()) {
  |                     FacesMessages.instance().restoreUnresolvedMessages();
  |             }
  |             return super.getActionURL(context, viewId);
  |     }
  | }
  | 

Relevant parts of modified org.jboss.seam.core.FacesMessages:

  | @Scope(ScopeType.CONVERSATION)
  | @Name("org.jboss.seam.core.facesMessages")
  | @Install(precedence = BUILT_IN)
  | @Intercept(NEVER)
  | public class FacesMessages implements Serializable {
  |     
  |         ...
  |     private Map<String, List<Message>> unresolvedFacesMessages = new 
HashMap<String, List<Message>>();
  | 
  |         ...
  | 
  |     @SuppressWarnings("unchecked")
  |     public void restoreUnresolvedMessages() {
  |             if (unresolvedFacesMessages == null || 
unresolvedFacesMessages.isEmpty())
  |                     return;
  |             for (Map.Entry<String, List<Message>> entry : 
unresolvedFacesMessages.entrySet()) {
  |                     for (Message msg : entry.getValue()) {
  |                             String clientId = getClientId(entry.getKey());
  |                             
FacesContext.getCurrentInstance().addMessage(clientId, msg.toFacesMessage());
  |                     }
  |             }
  |             unresolvedFacesMessages.clear();
  |     }
  |     
  |     public void addToControl(String id, FacesMessage facesMessage) {
  |             if (facesMessage != null) {
  |                     String clientId = getClientId(id);
  |                     List<Message> list = clientId != null ? 
getKeyedMessages(clientId)
  |                                     : getUnresolvedMessages(id);
  |                     list.add(new Message(facesMessage));
  |             }
  |     }
  | 
  |     private List<Message> getKeyedMessages(String clientId) {
  |             List<Message> list = keyedFacesMessages.get(clientId);
  |             if (list == null) {
  |                     list = new ArrayList<Message>();
  |                     keyedFacesMessages.put(clientId, list);
  |             }
  |             return list;
  |     }
  | 
  |     private List<Message> getUnresolvedMessages(String clientId) {
  |             List<Message> list = unresolvedFacesMessages.get(clientId);
  |             if (list == null) {
  |                     list = new ArrayList<Message>();
  |                     unresolvedFacesMessages.put(clientId, list);
  |             }
  |             return list;
  |     }
  | 
  |         ...
  | }
  | 

Does this seem like a plausible solution? The code seems to work, and 
validation messages get displayed correctly after redirects, which I think is 
quite nice from a usability point of view.

regards,
Karl

View the original post : 
http://www.jboss.com/index.html?module=bb&op=viewtopic&p=4127770#4127770

Reply to the post : 
http://www.jboss.com/index.html?module=bb&op=posting&mode=reply&p=4127770
_______________________________________________
jboss-user mailing list
[email protected]
https://lists.jboss.org/mailman/listinfo/jboss-user

Reply via email to