5 Replies Latest reply on Jan 17, 2008 12:29 PM by pmuir

    <ui:define> and injection

    schlafsack

      I've come up against a problem with using facelets templates and seam injection.

      I have the following two classes:

      @Name("query")
      @Scope(value = ScopeType.PAGE)
      public class Query
      {
       ...
      }
      


      @Name("search")
      @Scope(ScopeType.EVENT)
      public class Search
      {
       @In Query query;
       ...
      }
      


      I have a search widget (query.xhtml) that binds to the Query class:

      <ui:composition>
       <h:form>
       <h:inputText value="#{query.expression}" />
       <h:commandButton value="Search" />
       </h:form>
      </ui:composition>
      


      and a results page that runs the query and displays the results:

       <ui:include src="query.xhtml" />
       <div>
       <h:outputText value="Results are #{search.results}" />
       </div>
      


      Everything works fine until I use a ui:define tag to break up my page and use templates:

      <ui:composition template="searchTemplate.xhtml">
      
       <ui:define name="query">
       <ui:include src="query.xhtml" />
       </ui:define>
      
       <ui:define name="results">
       <h:outputText value="Results are #{search.results}"/>
       </ui:define>
      
      </ui:composition>
      


      When an element that binds to query and an element that binds to search are in separate <ui:define>'s I get a RequiredException:

      RequiredException: @In attribute requires non-null value: search.query

      Is this expected behaviour or am I not understanding the lifecycle of the pages properly?

        • 1. Re: <ui:define> and injection
          schlafsack

          I've found that this behaviour is dependent on the order of the <ui:insert> tags in the template. Everything works if query is <ui:insert>'ed before results but I get the exception if results is before query:

          <div id="container">
           <div><ui:insert name="results" /></div>
           <div><ui:insert name="query" /></div>
          </div>
          


          I guess this makes sense, results IS loaded before query is declared, but for some reason I thought the injection happened in a later phase.

          • 2. Re: <ui:define> and injection
            pmuir

            How are you creating the query object for injection, its not marked @AutoCreate or @In(create=true). N.b. All EL requests are create=true by default.

            • 3. Re: <ui:define> and injection
              schlafsack

              I was expecting the EL in the search widget (query.xhtml) to have created it when restoring the view after the post back. It appears that this happens as I expect if the widget is inserted into the same define tag or (as I've hacked it at the moment) if I simply put a comment with some EL in before the results list:

               <!-- #{query.expression} -->
               <ui:define name="results">
               <h:outputText value="Results are #{search.results}"/>
               </ui:define>
              


              Is it the case that if you are relying on EL to create your instances, then the order of the elements in the page is important?

              i.e. if A is injected into B then the element EL that creates A MUST come before the element EL that creates B

              • 4. Re: <ui:define> and injection
                schlafsack

                NB: In my case, the order of the elements in the template is important because they are involved in a CSS based three column layout.

                • 5. Re: <ui:define> and injection
                  pmuir

                  I'm not quite sure of your application architecture, but a PAGE scope component thats available in restore won't be available in render (you are in a different page scope by then).

                  Is it the case that if you are relying on EL to create your instances, then the order of the elements in the page is important?


                  Of course. *Something* needs to tell Seam to create the component, be it the component (@AutoCreate), the EL, or the @In(create=true).