9 Replies Latest reply on Feb 10, 2010 8:41 PM by detailleo

    How to invoke a method in a facelet tag

    haefti

      Hi!


      I like to use a facelet source tag as described in the wiki and invoke specific methods when using the tag.


      My problem is that the name of the action method is always interpreted as a name of a member (which of course can not be found in the bean).


      That's my code.


      cs.taglib.xml:


      <!DOCTYPE facelet-taglib PUBLIC 
        "-//Sun Microsystems, Inc.//DTD Facelet Taglib 1.0//EN"
        "http://java.sun.com/dtd/faces-taglib_1_0.dtd">
      
      <facelet-taglib>
         <namespace>http://mystuff</namespace>
         <tag>
            <tag-name>listButton</tag-name>
            <source>listButton.xhtml</source>
         </tag>
      </facelet-taglib>




      listButton.xhtml:


      <html xmlns="http://www.w3.org/1999/xhtml"
           xmlns:ui="http://java.sun.com/jsf/facelets"
           xmlns:h="http://java.sun.com/jsf/html">     
           <ui:composition>
                <h:commandButton value="Edit" action="#{assetManager[editAction]}" />
           </ui:composition>
      </html>




      If I now use the following code in my view


      <cs:listButton assetManager="#{pictureManager}" editAction="editPicture" />



      I get the following stack trace:


      javax.el.PropertyNotFoundException: Property 'editPicture' not found on type org.javassist.tmp.java.lang.Object_$$_javassist_2
           at javax.el.BeanELResolver$BeanProperties.get(BeanELResolver.java:193)
           at javax.el.BeanELResolver$BeanProperties.access$400(BeanELResolver.java:170)
      .
      .
      .



      The method pictureManager.editPicture() exists and is also declared in the interface so that should not be the problem. (Actually I changed the working code with a normal <h:commandButton action="#{pictureManager.editPicture}" /> to the code above.)


      I don't know what to do because to my mind I did pretty much the same just like in the wiki.


      Maybe someone could help we with it.


      Thanks!

        • 1. Re: How to invoke a method in a facelet tag
          gjeudy

          I'm not sure what else you may be doing in your code, I encourage you to get facelets source and run in debug mode to see what is going on exactly.


          From what I can guess from the partial stacktrace you provided it could be that facelets/JSF is looking to resolve a property (value expression) instead of a method expression. Therefore it might be looking at getEditPicture property instead of the method you are expecting.


          I have tried the method you explained using this:


          Facelets fits JSF like a glove


          and it worked fine for me. Alternatively you can look into inserting your button with an anonymous insert (see end of article to know how). This approach lets you insert virtually any facelet fragment inside your custom component at the defined position. (I usually prefer this method over the 

          backingBean[action]

          approach).


          • 2. Re: How to invoke a method in a facelet tag
            haefti

            First of all thanks for the answer.



            From what I can guess from the partial stacktrace you provided it could be that facelets/JSF is looking to resolve a property (value expression) instead of a method expression. Therefore it might be looking at getEditPicture property instead of the method you are expecting.


            That's exactly what I mean. Seam does not call the method editPicture() but looks for a getter.



            I have tried the method you explained using this:

            Facelets fits JSF like a glove

            I know the link but if I look here it seems to me that there is nothing different from my code.



            Alternatively you can look into inserting your button with an anonymous insert (see end of article to know how). This approach lets you insert virtually any facelet fragment inside your custom component at the defined position. (I usually prefer this method over the 
            backingBean[action]

            approach).


            That's where it gets interesting for me. ;)
            What do you mean by anonymous insert? Is it explained in the IBM article or in the facelets documentation? Is there a code example too?


            Sorry for my stupid questions but I don't how to get the stuff running with own tags and don't know what you mean by the anonymous insert either.


            Thanks for the help!

            • 3. Re: How to invoke a method in a facelet tag
              haefti

              Hi!


              Just to let you know how I fixed it.


              The problem occured due to working (!) code which was commented out (!!!).


              To make it clear here the not working code:



              <cs:listButtons assetManager="#{pictureManager}" editAction="editPicture" />
              
              <!--
              <h:commandButton type="submit" action="#{pictureManager.editPicture}" value="Edit" />
              -->



              Working code:


              <cs:listButtons assetManager="#{pictureManager}" editAction="editPicture" />
              
              <h:commandButton type="submit" action="#{pictureManager.editPicture}" value="Edit" />



              Normally I would say this is something for a JIRA task but I don't know exactly if such kind of behaviour may be intended.
              Actually I can't see any reason why code which is commented out has to be parsed  at all but interpreting working code as buggy can't be the solution.


              Maybe someone can tell me if this is really a bug and if it is a problem of Seam or facelets in general.


              Thanks!

              • 4. Re: How to invoke a method in a facelet tag

                Yeah, invocation of commented out components surprised me one day too.


                Solution is to add



                    <context-param>
                        <param-name>facelets.SKIP_COMMENTS</param-name>
                        <param-value>true</param-value>
                    </context-param>
                



                to web.xml. It will skip any

                <!-- -->

                content.

                • 5. Re: How to invoke a method in a facelet tag
                  haefti

                  Solution is to add
                      <context-param>
                          <param-name>facelets.SKIP_COMMENTS</param-name>
                          <param-value>true</param-value>
                      </context-param>
                  



                  to web.xml. It will skip any
                  <!-- -->

                  content.


                  Thanks!


                  Interestingly the documentation says that the default value should be true which is not the case in my Seam project.

                  • 6. Re: How to invoke a method in a facelet tag
                    gjeudy

                    Yes the anonymous insert approach is explained in the last part of the IBM article. It can be useful when you re-use a custom component but you don't know what actions will be on the button and moreover what type of button it could be or even how many buttons you wish to place inside your custom component.

                    • 7. Re: How to invoke a method in a facelet tag
                      parkylin

                      Hi everybody,


                      I used the solution Facelets fits JSF like a glove.
                      It works fine for me, but i have one question.
                      I notice that with that solution the outcome works fine if and only if I put the

                      backingBean[action]

                      parameter in my navigation rule.
                      Have you another solution? If I let the 'normal' navigation rule, my outcome success is never found...


                      Thanks

                      • 8. Re: How to invoke a method in a facelet tag
                        koelec

                        parkylin parkylin wrote on May 13, 2008 11:53:


                        Hi everybody,

                        I used the solution Facelets fits JSF like a glove.
                        It works fine for me, but i have one question.
                        I notice that with that solution the outcome works fine if and only if I put the
                        backingBean[action]

                        parameter in my navigation rule.
                        Have you another solution? If I let the 'normal' navigation rule, my outcome success is never found...

                        Thanks



                        I faced the same problem. It seams Seam uses the template action param value defined in the tag lib component to match against navigation from-action param. To circumvent the problem I came up with the following solution to share with you. Also see Max Katz blog about creating a reuseable confirmation dialog :
                        http://www.nofluffjuststuff.com/blog/max_katz/2008/11/richfaces_confirmation_dialog_3


                        <!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:a4j="http://richfaces.org/a4j"
                             xmlns:hvkl="http://prorail.nl/hvkl"
                             xmlns:rich="http://richfaces.org/rich" template="layout/template.xhtml">
                        
                             <ui:define name="body">
                                  <h:form>
                                       <a4j:jsFunction name="ok" action="#{userBean.save}" />
                                       <a4j:jsFunction name="cancel" action="#{userBean.cancel}" />
                                       <hvkl:confirm confirmId="confirm1" label="Saved" OKButtonValue="OK" cancelButtonValue="Annuleer" okFunction="ok()" cancelFunction="cancel()"/>
                                       <hvkl:confirm confirmId="confirm2" label="Saved2" OKButtonValue="OK" cancelButtonValue="Annuleer" okFunction="ok()" cancelFunction=""/>
                                  </h:form>
                             </ui:define>
                        </ui:composition>
                        


                        The trick is to pass the name of the javascript functions to invoke when the OK or Cancel button is clicked. These functions are defined in the calling code and can be separate or shared between multiple confirmation dialogs. Now we can setup different navigation rules on the actions defined in the calling code.


                        <?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">
                        <ui:component xmlns="http://www.w3.org/1999/xhtml"
                             xmlns:h="http://java.sun.com/jsf/html"
                             xmlns:a4j="http://richfaces.org/a4j"
                             xmlns:rich="http://richfaces.org/rich"
                             xmlns:f="http://java.sun.com/jsf/core"
                             xmlns:hvkl="http://prorail.nl/hvkl"
                             xmlns:ui="http://java.sun.com/jsf/facelets">
                        
                             <a4j:commandButton value="#{label}"
                                  onclick="#{rich:component(hvkl:concat(confirmId,'confirmation'))}.show();return false" />
                        
                             <rich:modalPanel id="#{confirmId}confirmation" width="250" height="150">
                                  <f:facet name="header">Confirmation</f:facet>
                                  <h:panelGrid>
                                       <h:panelGrid columns="2">
                                            <h:graphicImage value="/alert.png" />
                                            <h:outputText value="Are you sure?" style="FONT-SIZE: large;" />
                                       </h:panelGrid>
                                       <h:panelGroup>
                                            <rich:spacer height="20px" />
                                            <input type="button" value="#{OKButtonValue}"
                                                 onclick="#{rich:component(hvkl:concat(confirmId,'confirmation'))}.hide(); #{okFunction};return false;" />
                                            <input type="button" value="#{cancelButtonValue}"
                                                 onclick="#{rich:component(hvkl:concat(confirmId,'confirmation'))}.hide();#{cancelFunction};return false;" />
                                       </h:panelGroup>
                                  </h:panelGrid>
                             </rich:modalPanel>
                        </ui:component>
                        



                        The tag component definition uses the String concat function defined in a custom taglib as described in the post at the head of this thread.



                        • 9. Re: How to invoke a method in a facelet tag
                          detailleo

                          Hi!


                          I try to build a composite component, defining a facelet tag. Basically, the component have to show a button, with some text and an image :


                          In my taglib file, I declare the so called myButton component :


                          <tag>
                              <tag-name>myButton</tag-name>
                              <source>myButton.xhtml</source>
                          </tag>



                          and write it's definition into the myButton.xhtml file :




                          <!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"
                               xmlns:h="http://java.sun.com/jsf/html"
                               xmlns:f="http://java.sun.com/jsf/core"
                               xmlns:c="http://java.sun.com/jstl/core"
                               xmlns:ui="http://java.sun.com/jsf/facelets"
                               xmlns:a4j="http://richfaces.org/a4j"
                               xmlns:rich="http://richfaces.org/rich"
                               xmlns:s="http://jboss.com/products/seam/taglib">
                          
                          <!-- parameters  -->
                          <!-- componentName : a seam component name  -->
                          <!-- methodName : method name I want to invoke -->
                          
                          
                          <ui:composition>
                          
                               <span id="myButton_#{componentName}"> 
                          
                                    <h:graphicImage value="/img/#{methodName}.png"
                                              styleClass="textmiddle" />
                          
                                    <h:commandLink value="#{bundle[methodName]}"
                                              action="${componentName[methodName]}" />
                               </span>
                          </ui:composition>
                          </html>
                          




                          I would like that the h:commandLink bellow


                               <h:commandLink value="#{bundle[methodName]}"
                                    action="#{componentName[methodName]}" />



                          invokes the methodName method (first parameter of my facelet tag) of a Seam component called componentName (second parameter of my facelet tag).



                          Here is the way I use my tag, in my client page (mapped into the fuji namespace) :


                          <fuji:myButton componentName="portFolioManager" methodName="save"  />



                          This is intended to invoke the save method of the portFolioManager Seam component


                          The application starts correctly, the page loads successfuly but, when I click to the link, the following exception is thrown :


                          javax.el.MethodNotFoundException: /WEB-INF/facelets/tags/myButton.xhtml @27,47 action="${componentName[methodName]}": Method not found: portFolioManager.save()
                               com.sun.facelets.el.TagMethodExpression.invoke(TagMethodExpression.java:72)
                               javax.faces.component.MethodBindingMethodExpressionAdapter.invoke(MethodBindingMethodExpressionAdapter.java:88)
                               com.sun.faces.application.ActionListenerImpl.processAction(ActionListenerImpl.java:102)
                               javax.faces.component.UICommand.broadcast(UICommand.java:387)
                               org.ajax4jsf.component.AjaxViewRoot.processEvents(AjaxViewRoot.java:324)
                               org.ajax4jsf.component.AjaxViewRoot.broadcastEvents(AjaxViewRoot.java:299)
                               org.ajax4jsf.component.AjaxViewRoot.processPhase(AjaxViewRoot.java:256)
                               org.ajax4jsf.component.AjaxViewRoot.processApplication(AjaxViewRoot.java:469)
                               com.sun.faces.lifecycle.InvokeApplicationPhase.execute(InvokeApplicationPhase.java:82)
                               com.sun.faces.lifecycle.Phase.doPhase(Phase.java:100)
                               com.sun.faces.lifecycle.LifecycleImpl.execute(LifecycleImpl.java:118)
                               javax.faces.webapp.FacesServlet.service(FacesServlet.java:265)
                               org.jboss.seam.servlet.SeamFilter$FilterChainImpl.doFilter(SeamFilter.java:83)
                               org.jboss.seam.web.IdentityFilter.doFilter(IdentityFilter.java:40)
                               org.jboss.seam.servlet.SeamFilter$FilterChainImpl.doFilter(SeamFilter.java:69)
                               org.jboss.seam.web.MultipartFilter.doFilter(MultipartFilter.java:90)
                               org.jboss.seam.servlet.SeamFilter$FilterChainImpl.doFilter(SeamFilter.java:69)
                               org.jboss.seam.web.ExceptionFilter.doFilter(ExceptionFilter.java:64)
                               org.jboss.seam.servlet.SeamFilter$FilterChainImpl.doFilter(SeamFilter.java:69)
                               org.jboss.seam.web.RedirectFilter.doFilter(RedirectFilter.java:45)
                               org.jboss.seam.servlet.SeamFilter$FilterChainImpl.doFilter(SeamFilter.java:69)
                               org.ajax4jsf.webapp.BaseXMLFilter.doXmlFilter(BaseXMLFilter.java:178)
                               org.ajax4jsf.webapp.BaseFilter.handleRequest(BaseFilter.java:290)
                               org.ajax4jsf.webapp.BaseFilter.processUploadsAndHandleRequest(BaseFilter.java:388)
                               org.ajax4jsf.webapp.BaseFilter.doFilter(BaseFilter.java:515)
                               org.jboss.seam.web.Ajax4jsfFilter.doFilter(Ajax4jsfFilter.java:56)
                               org.jboss.seam.servlet.SeamFilter$FilterChainImpl.doFilter(SeamFilter.java:69)
                               org.jboss.seam.web.LoggingFilter.doFilter(LoggingFilter.java:60)
                               org.jboss.seam.servlet.SeamFilter$FilterChainImpl.doFilter(SeamFilter.java:69)
                               org.jboss.seam.web.HotDeployFilter.doFilter(HotDeployFilter.java:53)
                               org.jboss.seam.servlet.SeamFilter$FilterChainImpl.doFilter(SeamFilter.java:69)
                               org.jboss.seam.servlet.SeamFilter.doFilter(SeamFilter.java:158)
                               org.jboss.web.tomcat.filters.ReplyHeaderFilter.doFilter(ReplyHeaderFilter.java:96)
                          




                          The save method obviously exists in the portFolioManager Java interface, but is seem's that it can't be found.



                          Here are the first lines of the Java class that implement the Seam component :


                          @Local(IAbstractManager.class)
                          @Stateful
                          @Scope(SESSION)
                          @Name(value = "portFolioManager")
                          public class PortfolioManager extends AbstractManager<Portfolio>  {
                          ...
                          
                          



                          You can see the @Name(value = "portFolioManager") annotation that define the Seam component name (the one that is passed in the componentName facelet parameter)


                          The save method is actually implemented in the super class AbstractManager, that implements a Java interface AbstractManager (in which save is declared).


                          I know that the EL expression


                          backingBean[method]
                          



                          works and allow to pass action with plain JSF backing bean. But why doesn't it work with a Seam component ?


                          Am I missing something ?


                          Please help. I'm trying hard to solve this problem for hours now, reading documentation, browsing forums and source codes etc...


                          Being unable to do such a thing breaks the DRY principle in my pages, one thing that would make me completely crazy.


                          Octave