Details

    • Type: Improvement Improvement
    • Status: Open
    • Priority: Major Major
    • Resolution: Unresolved
    • Affects Version/s: 1.2, 2.0, 2.1
    • Fix Version/s: None
    • Component/s: Facelets/VDL
    • Labels:
      None
    • Status Whiteboard:
      Hide

      size_large importance_large

      Show
      size_large importance_large

      Description

      JB> 2) the "dynamic UI include" problem - This is a scenario where you
      JB> have several different types of include files and need to show a
      JB> list of items (each "item" being the include template) where the
      JB> list is only know at runtime. Let's make up a specific example here
      JB> to illustrate:

      JB> You have an event registration system that sells tickets online:

      JB> - A user can buy multiple tickets (and different types of tickets)
      JB> as part of the purchase process

      JB> - Each ticket purchased has to have totally different information
      JB> filled out

      JB> - There are many different types of tickets (e.g.: attendee,
      JB> sponsor, exhibitor, etc)

      JB> - every time they "add a ticket" (it considers what kind of ticket
      JB> they selected and adds that type of form to the list of forms that
      JB> will need to be completed before proceeding with the purchase)

      JB> Now obviously in this example you could build a "dynamic" template
      JB> that based on an input variable renders itself differently and put
      JB> this inside a <ui:repeat/> - or for that matter you could
      JB> programmatically insert the elements comprising each type of form
      JB> into the control tree. The problems with these approaches is that
      JB> you're back to server programming for every little UI change (not to
      JB> mention you've got one big component with potentially hundreds of
      JB> pieces of logic in it). ASP.NET solves this by allowing you to
      JB> define a .ascx file (much like a facelet include) and then
      JB> programmatically include them into something akin to a JSF
      JB> panelGroup. Then on postback you simply get an enumeration of the
      JB> items and loop through them to retrieve the values of the class
      JB> that's bound to the child components. Now with a lot of trickery and
      JB> a ton of time for each implementation you can get something close
      JB> with JSF but it's not at all intuitive or easy.

        Activity

        Hide
        lamine_ba added a comment - - edited
        • The JSF framework is a set of abstractions for building different types of web applications.
        • A business application is a set of polymorphic systems.
        • Different types of Customers
        • Different types of Contracts
        • Different types of Y

        which leads to this recurrent design

        public abstract class Y {
        }
        
        
        public class XY extends Y{
        }
        
        

        which once applied in an event registration system brings something like this

        public abstract class Ticket {         
        }
        
        
        public class SponsorTicket extends Ticket {
        }
        
        
        public class AttendeeTicket extends Ticket {
        }
        
        
        public class Reservation {
        
              public void addTicket(Ticket ticket) {
              }         
              public void removeTicket(Ticket ticket) {
              }
              public Collection<Ticket> getTickets() {
              }
         }
        
        

        Let's make something interesting now and let's override the toString() method to return the type of a ticket.

        public class SponsorTicket extends Ticket {
                
             public String toString() {
                    return "SponsorTicket";
             }
        }
        
        
        public class AttendeeTicket extends Ticket {
        
              public String toString() {
                     return "AttendeeTicket";
               }
        
        }
        
        

        This technique is powerful in a polymorphic system once coupled with the power of a template engine to generate dynamic content following a pattern like this one :

                     return-value${dynamic-value}
                     
        • Let's design now our form web pages using the Velocity Template Engine
        • Reservation page

        dynamic include of the derivate forms based on the toString() return value.

           #foreach($ticket in $reservation.tickets)
            #set($template="form" + $ticket + ".vm")         
            #include($template)
           #end
        
        • formSponsorTicket.vm
        label 1 : <input type="text" value=".."/>
        label 2 : <input type="text" value=".."/>
        label 3 : <input type="text" value=".."/>
        label 4 : <input type="text" value=".."/>
        label 5 : <input type="text" value=".."/>
        

        Because we have a polymorphic system, the forms will have some fields in common. Thus to avoid the duplication, it is better to create a common page (formTicket.vm) like we did for the abstract class Ticket.

        #foreach($ticket in $reservation.tickets)
              #include("formTicket.vm")                                                
              #set($template="form" + $ticket + ".vm")
              #include($template)
        #end
        
        

        ..............................................................................

        • Let's design now our form web pages using the Facelets Template Engine
        • Reservation Page
        
        <ui:repeat value="#{reservation.tickets}" var="ticket">
               <ui:include src="formTicket.xhtml"/>                 
               <ui:include src="form${ticket}.xhtml"/>                 
        </ui:repeat>
        
        

        Common Page : formTicket.xhtml

        
        label 1 : <h:inputText  value=".."/>
        label 2 : <h:inputText  value=".."/>
        
        

        Page : formSponsorTicket.xhtml

        
        label 3 : <h:inputText  value=".."/>
        label 4 : <h:inputText  value=".."/>
        label 5 : <h:inputText  value=".."/>
        
        

        -------------------------------------------------------------------------

        Comparison of the solutions

        The two solutions are the same even if I'm using two different template engines and what is quite amazing is that none of them works. Why? Because when you include a file in a iteration, you still get the whole context as model. No way to use the current object being iterated as model thus making you unable to do something like this

        <ui:repeat value="#{reservation.tickets}" var="ticket"> 
            <ui:include src="formTicket.xhtml"/> (var="ticket" not used)
            ....................................................
        </ui:repeat>
        

        Velocity :

        <input type="text" value="${ticket.property1}"/>
        

        Facelets :

         <h:inputText  value="#{ticket.property1}"/>
        

        Jason, You are doing a binding in your managed beans because you want to have a way to get this reference and as you may know, binding means coupling, maintenance nightmare, a change here and a change there.

        To overcome this problem without using the problematic programmatic approach, we should have a way to set the model to use for the template to be included, something like this

         <ui:include src="formTicket.xhtml" model="${ticket}"/>
        

        Reservation Page

        
        <ui:repeat value="#{reservation.tickets}" var="ticket">
             <ui:include src="formTicket.xhtml"    model="#{ticket}"/> 
             <ui:include src="form#{ticket}.xhtml"  model="#{ticket}"/>
        </ui:repeat>
        
        

        Another alternative would be to use the JSF composite components feature which is the opposite side of the dynamic include approach
        and one possible solution to leverage if the JSF runtime was able to generate dynamically the xml tags by following a pattern like this

          <namespace:#{tag-value}>
        

        So instead of creating a facelets view for each type of ticket, we will create a composite component for each type of ticket and write something like this in the reservation page

        Reservation Page

        <ui:repeat value="#{reservation.tickets}" var="ticket">
            <ez:formTicket    model="#{ticket}"/>   (formTicket.xhtml)
            <ez:form#{ticket}  model="#{ticket}"/>  (formSponsorTicket.xhtml...)
        </ui:repeat>
        

        Composite component (formSponsorTicket.xhtml)

        <composite:interface>
            <composite:attribute name="model" required="true"/>
        </composite:interface>
        
        <composite:implementation>
        
            label 3 : <h:inputText  value=" #{cc.attrs.model.property1}"/>
            label 4 : <inputText  value="#{cc.attrs.model.property2}"/>
            label 5 : <inputText  value="#{cc.attrs.model.property3}"/>
        
        </composite:implementation>
        
        

        If the dynamic tags generation is impossible for JSF, we can still make some if.........

        <ui:repeat value="#{reservation.tickets}" var="ticket">
              <c:if test="#{ticket == 'SponsorTicket'}">
                 <ez:formSponsorTicket    model="#{ticket}"/>.
               </c:if>
                   ......
        </ui:repeat>
        

        we have two solutions and two possible new issues.

        1) Facelets inclusion improvement
        2) EL support for generating dynamic xml tags

        These solutions have been realized from a page author perspective.

        Show
        lamine_ba added a comment - - edited The JSF framework is a set of abstractions for building different types of web applications. A business application is a set of polymorphic systems. Different types of Customers Different types of Contracts Different types of Y which leads to this recurrent design public abstract class Y { } public class XY extends Y{ } which once applied in an event registration system brings something like this public abstract class Ticket { } public class SponsorTicket extends Ticket { } public class AttendeeTicket extends Ticket { } public class Reservation { public void addTicket(Ticket ticket) { } public void removeTicket(Ticket ticket) { } public Collection<Ticket> getTickets() { } } Let's make something interesting now and let's override the toString() method to return the type of a ticket. public class SponsorTicket extends Ticket { public String toString() { return "SponsorTicket" ; } } public class AttendeeTicket extends Ticket { public String toString() { return "AttendeeTicket" ; } } This technique is powerful in a polymorphic system once coupled with the power of a template engine to generate dynamic content following a pattern like this one : return-value${dynamic-value} Let's design now our form web pages using the Velocity Template Engine Reservation page dynamic include of the derivate forms based on the toString() return value. #foreach($ticket in $reservation.tickets) #set($template= "form" + $ticket + ".vm" ) #include($template) #end formSponsorTicket.vm label 1 : <input type= "text" value= ".." /> label 2 : <input type= "text" value= ".." /> label 3 : <input type= "text" value= ".." /> label 4 : <input type= "text" value= ".." /> label 5 : <input type= "text" value= ".." /> Because we have a polymorphic system, the forms will have some fields in common. Thus to avoid the duplication, it is better to create a common page (formTicket.vm) like we did for the abstract class Ticket. #foreach($ticket in $reservation.tickets) #include( "formTicket.vm" ) #set($template= "form" + $ticket + ".vm" ) #include($template) #end .............................................................................. Let's design now our form web pages using the Facelets Template Engine Reservation Page <ui:repeat value= "#{reservation.tickets}" var= "ticket" > <ui:include src= "formTicket.xhtml" /> <ui:include src= "form${ticket}.xhtml" /> </ui:repeat> Common Page : formTicket.xhtml label 1 : <h:inputText value= ".." /> label 2 : <h:inputText value= ".." /> Page : formSponsorTicket.xhtml label 3 : <h:inputText value= ".." /> label 4 : <h:inputText value= ".." /> label 5 : <h:inputText value= ".." /> ------------------------------------------------------------------------- Comparison of the solutions The two solutions are the same even if I'm using two different template engines and what is quite amazing is that none of them works. Why? Because when you include a file in a iteration, you still get the whole context as model. No way to use the current object being iterated as model thus making you unable to do something like this <ui:repeat value= "#{reservation.tickets}" var= "ticket" > <ui:include src= "formTicket.xhtml" /> (var= "ticket" not used) .................................................... </ui:repeat> Velocity : <input type= "text" value= "${ticket.property1}" /> Facelets : <h:inputText value= "#{ticket.property1}" /> Jason, You are doing a binding in your managed beans because you want to have a way to get this reference and as you may know, binding means coupling, maintenance nightmare, a change here and a change there. To overcome this problem without using the problematic programmatic approach, we should have a way to set the model to use for the template to be included, something like this <ui:include src= "formTicket.xhtml" model= "${ticket}" /> Reservation Page <ui:repeat value= "#{reservation.tickets}" var= "ticket" > <ui:include src= "formTicket.xhtml" model= "#{ticket}" /> <ui:include src= "form#{ticket}.xhtml" model= "#{ticket}" /> </ui:repeat> Another alternative would be to use the JSF composite components feature which is the opposite side of the dynamic include approach and one possible solution to leverage if the JSF runtime was able to generate dynamically the xml tags by following a pattern like this <namespace:#{tag-value}> So instead of creating a facelets view for each type of ticket, we will create a composite component for each type of ticket and write something like this in the reservation page Reservation Page <ui:repeat value= "#{reservation.tickets}" var= "ticket" > <ez:formTicket model= "#{ticket}" /> (formTicket.xhtml) <ez:form#{ticket} model= "#{ticket}" /> (formSponsorTicket.xhtml...) </ui:repeat> Composite component (formSponsorTicket.xhtml) <composite:interface> <composite:attribute name= "model" required= "true" /> </composite:interface> <composite:implementation> label 3 : <h:inputText value= " #{cc.attrs.model.property1}" /> label 4 : <inputText value= "#{cc.attrs.model.property2}" /> label 5 : <inputText value= "#{cc.attrs.model.property3}" /> </composite:implementation> If the dynamic tags generation is impossible for JSF, we can still make some if......... <ui:repeat value= "#{reservation.tickets}" var= "ticket" > <c:if test= "#{ticket == 'SponsorTicket'}" > <ez:formSponsorTicket model= "#{ticket}" /> . </c:if> ...... </ui:repeat> we have two solutions and two possible new issues. 1) Facelets inclusion improvement 2) EL support for generating dynamic xml tags These solutions have been realized from a page author perspective.
        Hide
        lamine_ba added a comment - - edited

        JB> Proposed solutions certainly appear valid – but this may actually be a little easier. If item 989 (http://java.net/jira/browse/JAVASERVERFACES_SPEC_PUBLIC-989) is implemented you could quite simply use that function to programmatically add the dynamic include to the control tree. This of course assumes that you can read, compile and include a facelet file programmatically (which I believe I’ve seen somewhere?)

        MLB> Again Jason, thus you can programmatically create an UI representation for your object and attach this representation to its UI container
        But this time the UI representation is not created directly from your java code. You take it from a resource, from a facelets file written maybe by a web designer. And the algorithm of your handleRepeatItem(Object currentObjectFromCollection, UIComponent currentContainerHoldingChildComponents) method will look something like that:

        1)for the currentObjectFromCollection, find its UI resource file (facelets file)
        2) load the resource file, compile it and create an UI representation from it
        3) Attach the UI representation to its UI container : currentContainerHoldingChildComponents.add(representation)

        But at this point, If I were you, I would say " Hey I don't see anymore what JSF did for me and the abstraction it brings to ease the job. Why do I have my hands dirty? Why things shouldn't be simpler as to give the resource name to the JSF runtime which will manage the details for me and create an UI representation (UIComponent) from my resource ".

        javax.faces.Application class

        
        public abstract class Application {
        
         public UIComponent createComponent(FacesContext context,
                                               Resource componentResource) {
        }
        
        public UIComponent createComponent(Resource resource) {
          // a simplification of the first method which use the current context
        
        }
        
        
        public UIComponent createComponent(String resourceName) { 
        
         // unification, componentType and resourceName must be supported
        
        }
        
        }
        
        

        To move further Take a look at these issues below because they are related

        Where to find the resources : http://java.net/jira/browse/JAVASERVERFACES_SPEC_PUBLIC-809

        Jason, you are managing 40 developers to develop business applications? Are these applications modular? How do you divide the work among your developers? How do you do the integration of the pieces (modules)? How do you reuse your past work when developing a new application? Do you always start from scratch?

        Support for modular, reusable fragments of web applications : http://java.net/jira/browse/JAVASERVERFACES_SPEC_PUBLIC-532
        Plugin System : http://java.net/jira/browse/JAVASERVERFACES_SPEC_PUBLIC-970

        Have you a web designer in your team?

        Multi-templating : http://java.net/jira/browse/JAVASERVERFACES_SPEC_PUBLIC-971

        Show
        lamine_ba added a comment - - edited JB> Proposed solutions certainly appear valid – but this may actually be a little easier. If item 989 ( http://java.net/jira/browse/JAVASERVERFACES_SPEC_PUBLIC-989 ) is implemented you could quite simply use that function to programmatically add the dynamic include to the control tree. This of course assumes that you can read, compile and include a facelet file programmatically (which I believe I’ve seen somewhere?) MLB> Again Jason, thus you can programmatically create an UI representation for your object and attach this representation to its UI container But this time the UI representation is not created directly from your java code. You take it from a resource, from a facelets file written maybe by a web designer. And the algorithm of your handleRepeatItem(Object currentObjectFromCollection, UIComponent currentContainerHoldingChildComponents) method will look something like that: 1)for the currentObjectFromCollection, find its UI resource file (facelets file) 2) load the resource file, compile it and create an UI representation from it 3) Attach the UI representation to its UI container : currentContainerHoldingChildComponents.add(representation) But at this point, If I were you, I would say " Hey I don't see anymore what JSF did for me and the abstraction it brings to ease the job. Why do I have my hands dirty? Why things shouldn't be simpler as to give the resource name to the JSF runtime which will manage the details for me and create an UI representation (UIComponent) from my resource ". javax.faces.Application class public abstract class Application { public UIComponent createComponent(FacesContext context, Resource componentResource) { } public UIComponent createComponent(Resource resource) { // a simplification of the first method which use the current context } public UIComponent createComponent( String resourceName) { // unification, componentType and resourceName must be supported } } To move further Take a look at these issues below because they are related Where to find the resources : http://java.net/jira/browse/JAVASERVERFACES_SPEC_PUBLIC-809 Jason, you are managing 40 developers to develop business applications? Are these applications modular? How do you divide the work among your developers? How do you do the integration of the pieces (modules)? How do you reuse your past work when developing a new application? Do you always start from scratch? Support for modular, reusable fragments of web applications : http://java.net/jira/browse/JAVASERVERFACES_SPEC_PUBLIC-532 Plugin System : http://java.net/jira/browse/JAVASERVERFACES_SPEC_PUBLIC-970 Have you a web designer in your team? Multi-templating : http://java.net/jira/browse/JAVASERVERFACES_SPEC_PUBLIC-971
        Hide
        Ed Burns added a comment -

        Set priority to baseline ahead of JSF 2.3 triage. Priorities will be assigned accurately after this exercise.

        Show
        Ed Burns added a comment - Set priority to baseline ahead of JSF 2.3 triage. Priorities will be assigned accurately after this exercise.

          People

          • Assignee:
            Unassigned
            Reporter:
            Ed Burns
          • Votes:
            1 Vote for this issue
            Watchers:
            1 Start watching this issue

            Dates

            • Created:
              Updated: