6 Replies Latest reply on Sep 25, 2007 11:09 AM by schmod54

    form controls with the same property name

      How do I add error messages to a particular control if multiple controls share the same property name on the same page? E.g., say I have a user registration form, where usernames are unique. If a user enters a duplicate username, in my action bean I can add an error like so:

      facesMessages.addToControlFromResourceBundle("username", "usernameExists");


      But say I have a batch user registration process that allows someone to register 10 users at a time. If I use the same code, then the error message gets displayed at the top of the page and the user doesn't know which username is invalid. I could change the error message to contain the invalid name, but I would rather display it next to the relevant control. So how do I do that? Thanks!

        • 1. Re: form controls with the same property name
          wise_guybg

          Normally you wouldn't have components with the same name. Why don't you add an index next to the name. Ex.: username1, username2...

          Either way if you want them referenced from/to a backing bean you should have different fields!?

          • 2. Re: form controls with the same property name

            My initial attempt was to do as you said... but it didn't work. I've played around with it and discovered that displaying faces messages attached to a control inside of a loop is problematic. Has anyone done this? The following is my code to allow users to modify a set of "Category" objects:

            <a4j:repeat value="#{batchCategories}" var="cat">
             <ui:param name="i" value="#{batchCategories.rowIndex}"/>
             <s:decorate id="name#{i}Decorate" template="/inc/Field.xhtml">
             <ui:define name="label">Name:</ui:define>
             <h:inputText id="name#{i}" value="#{cat.name}"/>
             </s:decorate>
            </a4j:repeat>


            Instead of getting my form error messages displayed attached to the proper controls, I get:

            WARNING: FacesMessage(s) have been enqueued, but may not have been displayed.


            in my logs. My decorator is fine, and works if I hardwire it to a particular item, like so:

            <ui:param name="cat" value="#{batchCategories.rowData}"/>
            <ui:param name="i" value="#{batchCategories.rowIndex}"/>
            <s:decorate id="name#{i}Decorate" template="/inc/Field.xhtml">
             <ui:define name="label">Name:</ui:define>
             <h:inputText id="name#{i}" value="#{cat.name}"/>
            </s:decorate>


            <ui:repeat> has the same behavior, except no warning in the logs. <c:forEach> doesn't display properly either... it puts all the messages in the global area. So... does anyone know how to display faces messages next to particular form controls inside a loop?

            • 3. Re: form controls with the same property name
              wise_guybg

              I'm wondering about that

              batchCategories.rowIndex


              How have you realized it?

              I also did not see how you have called
              facesMessages.addToControlFromResourceBundle("username", "usernameExists");


              You should be using indexes there too, right?

              • 4. Re: form controls with the same property name

                I was a bit ambiguous in my previous posts, because I was mixing examples... also I was wrong about the exact problems I've had with different approaches. So I've made a simplified example that shows what I want to do, and how the different approaches fail. I'm running this in a seam-genned project on seam 2 beta, jboss 4.2.1.GA, but I expect it to behave the same way in other versions. Here is the source:

                I have the following in my pages.xml... It's not strictly necessary to run the example though:


                <page view-id="/control_loop_test.xhtml">
                 <begin-conversation join="true"/>
                 </page>



                Here is an action bean that assigns facesMessages to form controls inside a loop:
                import org.jboss.seam.ScopeType;
                import org.jboss.seam.annotations.In;
                import org.jboss.seam.annotations.Name;
                import org.jboss.seam.annotations.Scope;
                import org.jboss.seam.annotations.datamodel.DataModel;
                import org.jboss.seam.faces.FacesMessages;
                
                import java.util.Arrays;
                import java.util.Collection;
                
                @Name("controlLoop")
                @Scope(ScopeType.CONVERSATION)
                public class ControlLoopAction {
                
                 @In
                 private FacesMessages facesMessages;
                
                 public static class MyEntity {
                 private String name;
                
                 public String getName() {
                 return name;
                 }
                
                 public void setName(String name) {
                 this.name = name;
                 }
                 }
                
                 @DataModel
                 private Collection<MyEntity> batchEntities = Arrays.asList(new MyEntity(), new MyEntity());
                
                 public Collection<MyEntity> getBatchEntities() {
                 return batchEntities;
                 }
                
                 public String submit(String controlPrefix) {
                 for (int i = 0; i < batchEntities.size(); i++)
                 facesMessages.addToControlFromResourceBundle(controlPrefix + "Name" + i, "badName" + i);
                 return "/control_loop_test.xhtml";
                 }
                }


                Here is a page that tries a few different ways of inputting data into the action bean.

                <?xml version="1.0" encoding="UTF-8"?>
                <!DOCTYPE html
                 PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
                 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
                <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"
                 xmlns:a4j="https://ajax4jsf.dev.java.net/ajax"
                 xmlns:c="http://java.sun.com/jsp/jstl/core"
                 xmlns:h="http://java.sun.com/jsf/html"
                 xmlns:s="http://jboss.com/products/seam/taglib"
                 xmlns:ui="http://java.sun.com/jsf/facelets">
                <head>
                 <title>Control Loop Test</title>
                </head>
                <body>
                controlLoop: #{controlLoop}<br/>
                global messages: <h:messages globalOnly="true"/><br/>
                hardwired form... this works (at least for h:message anyway) but I really don't want to have to do this for large variable-size forms:
                <h:form id="hardwiredForm">
                 Name0:
                 <h:inputText id="hardwiredName0" value="#{controlLoop.batchEntities[0].name}"/>
                 s:message: <s:message/> h:message: <h:message for="hardwiredName0"/><br/>
                 Name1:
                 <h:inputText id="hardwiredName1" value="#{controlLoop.batchEntities[1].name}"/>
                 s:message: <s:message/> h:message: <h:message for="hardwiredName1"/><br/>
                 <h:commandButton value="Submit" action="#{controlLoop.submit('hardwired')}"/>
                </h:form>
                <br/>
                a4j:repeat form... I'm thinking this doesn't work because the id of h:inputText gets resolved when the view is built... see
                <a href="http://www.ninthavenue.com.au/blog/c:foreach-vs-ui:repeat-in-facelets">
                 http://www.ninthavenue.com.au/blog/c:foreach-vs-ui:repeat-in-facelets</a>:<br/>
                <h:form id="a4jRepeatForm">
                 <a4j:repeat id="mainLoop" value="#{controlLoop.batchEntities}" var="myEntity" rowKeyVar="i">
                 Name#{i}:
                 <h:inputText id="a4jName#{i}" value="#{myEntity.name}"/>
                 s:message: <s:message/> h:message: <h:message for="a4jName#{i}"/><br/>
                 </a4j:repeat>
                 <h:commandButton value="Submit" action="#{controlLoop.submit('a4j')}"/>
                </h:form>
                <br/>
                ui:repeat form... same problem as a4j:repeat, but I don't want to use ui:repeat anyway because of its known bugs:
                <h:form id="uiRepeatForm">
                 <ui:repeat value="#{batchEntities}" var="myEntity">
                 Name#{batchEntities.rowIndex}:
                 <h:inputText id="uiName#{batchEntities.rowIndex}" value="#{myEntity.name}"/>
                 s:message: <s:message/> h:message: <h:message for="uiName#{batchEntities.rowIndex}"/><br/>
                 </ui:repeat>
                 <h:commandButton value="Submit" action="#{controlLoop.submit('ui')}"/>
                </h:form>
                <br/>
                c:forEach form... maybe this would work if I could get c: tags working... I tried some things I found on the web to get c: tags working, but none of them worked for me:<br/>
                <h:form id="cForEachForm">
                 <c:forEach id="mainLoop" value="#{controlLoop.batchEntities}" var="myEntity" varStatus="i">
                 Name#{i.index}:
                 <h:inputText id="forEachName#{i.index}" value="#{myEntity.name}"/>
                 s:message: <s:message/> h:message: <h:message for="forEachName#{i.index}"/><br/>
                 </c:forEach>
                 <h:commandButton value="Submit" action="#{controlLoop.submit('forEach')}"/>
                </h:form>
                
                </body>
                </html>


                If anyone could show me how to get the facesMessages to correctly appear next to the relevant controls within the context of a loop, I'd greatly appreciate it. Thanks.

                • 5. Re: form controls with the same property name
                  pmuir
                  • 6. Re: form controls with the same property name

                    Thanks. I voted for it.