5 Replies Latest reply on Feb 20, 2010 9:41 AM by shadders

    Passing and entity to a template fails on submit

    shadders

      I've got a project that started with seam-gen (from eclipse) which I'm trying to modify.
      eclipse 3.5, JBoss tools 3.1, JBoss AS 5.1, seam 2.2.0 GA


      I have an entity Person which has a property Address which is also an entity.  Actually it has two, address1 and address2.


      The PersonHome/PersonList/AddressHome/AddressList beans are unmodified from the generated version.


      I'm trying to build a person edit form and use a template for displaying the address because I will want to display the address from numerous sources (I have another entity 'Creature' which has address as well).  So I am passing the address entity as a parameter to the template.


      The relevant section of the main form is:


      <rich:tabPanel switchType="client">
           <rich:tab id="address1Detail" label="Main Address">
                <f:facet name="header">Primary Address</f:facet>
                <s:decorate id="address1Fields" template="/comp/Address.xhtml"
                     propagation="nest">
                     <ui:param name="addressBean" value="#{personHome.instance.addressByAddress1}" />
                     <ui:param name="addressHomeBean" value="#{addressHome}" />
                </s:decorate>
           </rich:tab>
           <rich:tab id="address2Detail" label="Secondary Address">
                <f:facet name="header">Secondary Address (click to expand)</f:facet>
                <s:decorate id="address2Fields" template="/comp/Address.xhtml"
                     propagation="nest">
                     <ui:param name="addressBean" value="#{personHome.instance.addressByAddress2}" />
                     <ui:param name="addressHomeBean" value="#{addressHome}" />
                </s:decorate>
           </rich:tab>
      </rich:tabPanel>
      



      and the template:


      <!DOCTYPE composition PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
          "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
      <ui:composition xmlns="http://www.w3.org/1999/xhtml"
           xmlns:s="http://jboss.com/products/seam/taglib"
           xmlns:ui="http://java.sun.com/jsf/facelets"
           xmlns:f="http://java.sun.com/jsf/core"
           xmlns:h="http://java.sun.com/jsf/html"
           xmlns:rich="http://richfaces.org/rich"
           xmlns:a="http://richfaces.org/a4j"
          >
      
           <s:conversationPropagation propagation="join"/>
           <h:outputText value="#{conversation.id}" />
           <s:decorate id="street1Field" template="/layout/edit.xhtml" rendered="#{addressHomeBean.allowView}">
                <ui:define name="label">Street1</ui:define>
                <h:outputText id="vStreet1" value="#{addressBean.street1}" rendered="#{addressHomeBean.allowViewNotEdit}"/>
                <h:inputText id="sStreet1" required="true" size="45" maxlength="45"
                     value="#{addressBean.street1}" rendered="#{addressHomeBean.allowEdit}">
                     <a:support event="onblur" reRender="street1Field"
                          bypassUpdates="true" ajaxSingle="true" />
                </h:inputText>
           </s:decorate>
      
           <s:decorate id="street2Field" template="/layout/edit.xhtml" rendered="#{addressHomeBean.allowView}">
                <ui:define name="label">Street2</ui:define>
                <h:outputText id="vetreet2" value="#{addressBean.street2}" rendered="#{addressHomeBean.allowViewNotEdit}"/>
                <h:inputText id="eStreet2" size="45" maxlength="45"
                     value="#{addressBean.street2}" rendered="#{addressHomeBean.allowEdit}">
                     <a:support event="onblur" reRender="street2Field"
                          bypassUpdates="true" ajaxSingle="true" />
                </h:inputText>
           </s:decorate>
      
           <s:decorate id="suburbField" template="/layout/edit.xhtml" rendered="#{addressHomeBean.allowMinorView}">
                <ui:define name="label">Suburb</ui:define>
                <h:outputText id="vSuburb" value="#{addressBean.suburb}" rendered="#{addressHomeBean.allowMinorViewNotEdit}"/>
                <h:inputText id="eSuburb" required="true" size="45" maxlength="45"
                     value="#{addressBean.suburb}" rendered="#{addressHomeBean.allowEdit}">
                     <a:support event="onblur" reRender="suburbField" bypassUpdates="true"
                          ajaxSingle="true" />
                </h:inputText>
           </s:decorate>
      
           <s:decorate id="cityField" template="/layout/edit.xhtml" rendered="#{addressHomeBean.allowMinorView}">
                <ui:define name="label">City</ui:define>
                <h:outputText id="vCity" value="#{addressBean.city}" rendered="#{addressHomeBean.allowMinorViewNotEdit}"/>
                <h:inputText id="eCity" size="45" maxlength="45"
                     value="#{addressBean.city}" rendered="#{addressHomeBean.allowEdit}">
                     <a:support event="onblur" reRender="cityField" bypassUpdates="true"
                          ajaxSingle="true" />
                </h:inputText>
           </s:decorate>
      
           <s:decorate id="postcodeField" template="/layout/edit.xhtml" rendered="#{addressHomeBean.allowMinorView}">
                <ui:define name="label">Postcode</ui:define>
                <h:outputText id="vPostcode" value="#{addressBean.postcode}" rendered="#{addressHomeBean.allowMinorViewNotEdit}"/>
                <h:inputText id="ePostcode" required="true" size="10" maxlength="10" readonly="true" disabled="true"
                     value="#{addressBean.postcode}" rendered="#{addressHomeBean.allowEdit}" >
                     <a:support event="onblur" reRender="postcodeField"
                          bypassUpdates="true" ajaxSingle="true" />
                </h:inputText>
           </s:decorate>
      
           <s:decorate id="stateField" template="/layout/edit.xhtml" rendered="#{addressHomeBean.allowMinorView}">
                <ui:define name="label">State</ui:define>
                <h:outputText id="vState" value="#{addressBean.state}" rendered="#{addressHomeBean.allowMinorViewNotEdit}"/>
                <h:inputText id="eState" size="20" maxlength="20"
                     value="#{addressBean.state}" rendered="#{addressHomeBean.allowEdit}">
                     <a:support event="onblur" reRender="stateField" bypassUpdates="true"
                          ajaxSingle="true" />
                </h:inputText>
           </s:decorate>
      
           <div style="clear: both"><span class="required">*</span> required
           fields</div>
      
      </ui:composition>
      



      This works fine for the initial display.  I can see both of the address values populated correctly.  But on any form submit of the personEdit page (e.g. toggling a simpleTogglePanel or clicking the save button) it throws me to a debug page with the exception:


      "Exception during request processing:
      Caused by javax.servlet.ServletException with message: "/comp/Address.xhtml @18,75 value="#{addressBean.street1}": Target Unreachable, identifier 'addressBean' resolved to null""



      If I make a second template that references the address bean directly from personHome e.g.




      <s:decorate id="street1Field" template="/layout/edit.xhtml"
           rendered="#{addressHome.allowView}">
           <ui:define name="label">Street1</ui:define>
           <h:outputText id="vStreet1"
                value="#{personHome.instance.addressByAddress1.street1}" rendered="#{addressHome.allowViewNotEdit}" />
           <h:inputText id="sStreet1" required="true" size="45"
                maxlength="45" value="#{personHome.instance.addressByAddress1.street1}"
                rendered="#{addressHome.allowEdit}">
                <a:support event="onblur" reRender="street1Field"
                     bypassUpdates="true" ajaxSingle="true" />
           </h:inputText>
      </s:decorate>



      It works fine but of course that makes the template pointless since I need a seperate one for address1 and address2.


      It seems that the ui:param doesn't reevaluate on an ajax update or a full page update.  I've tried all sorts of combinations of propagation values.  I can see that the conversation is being passed to the template because if I use second template I can output the conversation ID and see it is the same as the main form.  I'm not sure if this is conversation related at all though?


      Can anyone help?  I'm quite new to seam/JSF so if this is the wrong approach to what I'm trying to achieve please put me on the right track.

















        • 1. Re: Passing and entity to a template fails on submit
          shadders

          Perhaps a better way to put the question is in terms of what I want to achieve:


          What is the best way to create an Edit template (for insertion in a page) for an Entity that is referenced from multiple places.


          i.e. The address Entity is a property of multiple other entities.  Person, Creature, Vet etc... All with their respective EntityHome and EntityList components.  So how do I avoid having to make an AddressEdit form for each one?

          • 2. Re: Passing and entity to a template fails on submit
            shadders

            Found this in section 7.11 of seam reference:





            7.11. Conversational components and JSF component
            bindings
            Conversational components have one minor limitation: they cannot be used to hold bindings to
            JSF components. (We generally prefer not to use this feature of JSF unless absolutely necessary,
            since it creates a hard dependency from application logic to the view.) On a postback request,
            component bindings are updated during the Restore View phase, before the Seam conversation
            context has been restored.

            I don't really understand it but I've got a gut feeling this is related to my problem?

            • 3. Re: Passing and entity to a template fails on submit
              shadders
              ok I think I've figured this much out...

              during the postback restore_view phase the main edit form is evaluating this line to pass to the Address.xhtml template



              "<ui:param name="addressBean" value="#{personHome.instance.addressByAddress1}" />"



              At this point the conversational context is not available so personHome.instance.addressByAddress1 resolves to null.  Apparently on the first view of the page it's done in a different order?

              in a later phase the EL expressions in the address template are being evaluated.  The conversational components are available then but it's too late because the ui:param was already passed and evaluated.  So the component tree builds but can't render.

              I think I understand the problem now but I've still got no idea how to get a round it.  The seam reference talks about creating event scoped backing beans but I'm not quite understanding that.  I though about passing page parameters instead (the address ID's so the template could retreive them from the ID) but that doesn't really help because I would need to pass an address1-ID and an address2-ID so I'd still need to double up the template code.

              • 4. Re: Passing and entity to a template fails on submit
                shadders

                Now I'm really confused.  I just noticed if I comment out the address section of the main edit page it works ok.  That's confusing because I've done exactly the same thing with the person bean i.e. passed it to a person.xhtml template and that seems to work fine on a postback.




                               <s:decorate id="usernameField" template="/layout/edit.xhtml" rendered="#{personHomeBean.allowMinorView}">
                                <ui:define name="label">Username</ui:define>
                                <h:outputText id="vUsername" value="#{personBean.username}" rendered="#{personHomeBean.allowMinorViewNotEdit}"/>
                                    <h:inputText id="username" required="true" size="45" maxlength="45" value="#{personBean.username}"
                                         rendered="#{personHomeBean.allowEdit}">
                                    <a:support event="onblur" reRender="firstNameField" bypassUpdates="true" ajaxSingle="true" />
                                </h:inputText>
                            </s:decorate>







                                              <s:decorate id="personFields" template="/comp/Person.xhtml">
                                                   <ui:param name="personBean" value="#{personHome.instance}" />
                                                   <ui:param name="personHomeBean" value="#{personHome}" />
                                              </s:decorate>







                • 5. Re: Passing and entity to a template fails on submit
                  shadders

                  Narrowed it down a bit further.


                  It turns out the problem only occurs when one of the two address entities is null.  I started writing the address entity state to the log (i.e. if null or not).  What I can't figure out is that on the initial page it says that address2 is null but the page renders ok.  I would have thought I should get the same message.  It's almost like it's creating a blank address bean for rendering the page then nulling it?  On an ajax update (postback?) I get the original exception.


                  I tried modifying Person to make sure to create a blank address if it's null (in the setter and constructor) but that seems to cause problems for hibernate.


                  I don't want to use NotNull on that field because it should be nullable.


                  I could just do a nullcheck before rendering but the trouble is if it is null I want the form to appear so the user can enter a new address if they want to?


                  any ideas?