4 Replies Latest reply on Jul 31, 2009 2:08 PM by roykachouh

    Extending a richfaces component (HtmlDataScroller).

    roykachouh

      I was just wondering if someone can help me getting started in extending the datascroller component. I would like to have my own version of the component that will not require holding all the elements in a collection at one time in order to display the number of pages.

      In other words, I was looking at the UIDatascroller source, and I see that the getPageCount method is used in determining the number of pages to be used in the datascroller. I would like to have a class that overides this method, with my own computation. Further, I would like to attach a listener to the datascroller. The listener will intercept each page request on the scroller, and it will determine whether or not the data needs to be refreshed with new data. Ideally, I would like to only keep 3 pages worth of data in memory.

      What is the best way to accomplish this? Would it be creating my own JSF component that Just extends the HtmlDataScroller class? Or is there something simpler that I am missing.

        • 1. Re: Extending a richfaces component (HtmlDataScroller).
          roykachouh

          For all those who care, I came up with a pretty elegant solution to this...

          Here are the steps I've taken...

          1st - Create a class that extends com.sun.facelets.tag.AbstractTagLibrary.

          This class will be used to create our component library. Also create a component handler class...

          Here is my class...

          public class LtpLibrary extends AbstractTagLibrary {
          
           public final static String Namespace = "http://mysite.com/core";
          
           public LtpLibrary() {
           super(Namespace);
          
           this.addConverter("genericSubstringConverter",GenericSubstringConverter.CONVERTER_ID,GenericSubstringConverterHandler.class);
          
           this.addComponent("ltpDataScroller",LtpDataScroller.COMPONENT_ID,"org.richfaces.DataScrollerRenderer",LtpDataScrollerHandler.class);
          
           Method[] methods = LTPFunctions.class.getMethods();
          
           for (int i = 0; i < methods.length; i++) {
           if (Modifier.isStatic(methods.getModifiers())) {
           this.addFunction(methods.getName(), methods);
           }
           }
          
           }
          
           }
          
           public class LtpDataScrollerHandler extends ComponentHandler {
          
           public LtpDataScrollerHandler(ComponentConfig config) {
           super(config);
           }
          
           @Override
           protected UIComponent createComponent(FaceletContext ctx) {
           return ctx.getFacesContext().getApplication().createComponent(LtpDataScroller.COMPONENT_ID);
           }
          
           }
          

          The component that I create is called ltpDataScroller

          2nd - Create a class that extends the Richfaces HtmlDataScroller
          public class LtpDataScroller extends HtmlDatascroller {
          
           public static final String COMPONENT_ID = "com.asc.ltp.web.components.LtpDataScroller";
          
           @Override
           public int getPageCount() {
          //here is where you will put actual logic to compute the pages needed for //the datascroller
           return 100;
           }
          
          
          }
          


          3rd - Add the following context param in web.xml, and don't forget the tld file in META-INF
          <context-param>
           <param-name>facelets.LIBRARIES</param-name>
           <param-value>/META-INF/ltp-taglib.xml</param-value>
           </context-param>
          


          Tld file..
          <facelet-taglib>
           <library-class>com.asc.ltp.tags.library.LtpLibrary</library-class>
          </facelet-taglib>
          


          Create a custom scroller listener to listen to the scrolling events...the listener will be responsible for refreshing data in the actual datascroller..
          public void processScroller(DataScrollerEvent event) {
           LOG.debug(event);
          
           }
          

          Finally, use the newly created component...

          <ui:composition xmlns="http://www.w3.org/1999/xhtml"
           xmlns:ui="http://java.sun.com/jsf/facelets"
           xmlns:h="http://java.sun.com/jsf/html"
           xmlns:f="http://java.sun.com/jsf/core"
           xmlns:ltp="http://mysite.com/core"
           xmlns:a4j="https://ajax4jsf.dev.java.net/ajax"
           xmlns:fn="http://java.sun.com/jsp/jstl/functions"
           xmlns:rich="http://richfaces.org/rich"
           xmlns:c="http://java.sun.com/jsp/jstl/core">
           <ltp:ltpDataScroller scrollerListener="#{ParkApi.commentScrollerListener}" for="commentGrid"/>
          </ui:composition>
          


          Hope this helps!

          -Roy

          • 2. Re: Extending a richfaces component (HtmlDataScroller).
            roykachouh

            I am actually now having issues with this approach...I guess I jumped the gun with this soultion...

            the issue i am now having is this...


            I am trying to extend the datascroller richfaces component. I want to add some of my own behavior to this component. So what I did was I added a component in my application library by extending the abstracttaglibrary as so...

            public class LtpLibrary extends AbstractTagLibrary {
            
             public final static String Namespace = "http://mysite.com/core";
            
             public LtpLibrary() {
             super(Namespace);
            
            
             this.addComponent("ltpDataScroller",LtpDataScroller.COMPONENT_ID,"org.richfaces.DataScrollerRenderer",LtpDataScrollerHandler.class);
            
            
             }
            
            }
            
            


            Inside my custom LTP datascroller, I have a custom pagination handler attribute that gets set. The attribute gets set fine on the initial request, however when I try to page thru the dataset by clicking on page 2, the attribute does not get set, and a null pointer exception is thrown as a result. I will add as much code as possible to make clear the issue.

            My AjaxComponentHandler...
            public class LtpDataScrollerHandler extends AjaxComponentHandler {
            
             private final TagAttribute scrollerListener;
             private final TagAttribute paginator;
             public LtpDataScrollerHandler(ComponentConfig config) {
             super(config);
             this.scrollerListener = this.getAttribute("scrollerListener");
             this.paginator = this.getAttribute("paginator");
             }
            
             @Override
             protected UIComponent createComponent(FaceletContext ctx) {
             return ctx.getFacesContext().getApplication().createComponent(LtpDataScroller.COMPONENT_ID);
             }
            
             @Override
             protected void setAttributes(FaceletContext ctx, Object obj) {
             LtpDataScroller scroller = (LtpDataScroller)obj;
             ExpressionFactory expressionFactory = ctx.getExpressionFactory();
            
             if(this.scrollerListener!=null){
             MethodExpression expression = expressionFactory.createMethodExpression(ctx, scrollerListener.getValue(), null, new Class[] {DataScrollerEvent.class});
             scroller.setScrollerListener(expression);
             }
             if(this.paginator!=null){
             ValueExpression ve = expressionFactory.createValueExpression(ctx, paginator.getValue(), Paginator.class);
             Paginator paginator = (Paginator) ve.getValue(ctx);
             scroller.setPaginator(paginator);
             }
            
            
             }
            
             @Override
             protected MetaRuleset createMetaRuleset(Class type) {
             return super.createMetaRuleset(type);
             }
            }
            


            The class that extends the HTMLDataScroller...
            public class LtpDataScroller extends HtmlDatascroller{
            
             public static final String COMPONENT_ID = "com.asc.ltp.web.components.LtpDataScroller";
             private Paginator paginator;
             @Override
             public int getPageCount() {
             int pageCount = 0;
            
             PaginationConfig config = new DefaultPaginationConfig(5,3);
            
             pageCount = paginator.determinePageCount(config);
            
             return pageCount;
             }
            
             public Paginator getPaginator() {
             return paginator;
             }
             public void setPaginator(Paginator paginator) {
             this.paginator = paginator;
             }
            
             /*@Override
             public boolean isRendered() {
             return paginator.shouldRender();
             }*/
            }
            


            Is there something I am missing? Why does my paginator attribute get set on the inital rendering, but does not for subsequent ajax requests?

            Your help is really appreciated on this one...

            • 3. Re: Extending a richfaces component (HtmlDataScroller).
              nbelaevski

              Hi,

              Take a look at documentation about StateHolder interface; especially saveState/restoreState methods.

              • 4. Re: Extending a richfaces component (HtmlDataScroller).
                roykachouh

                Thats great! Worked like a charm, but now I am stuck on another issue...

                My Pagination interface is working as expected, when a user clicks on a page in the datascroller, my custom datascroller component, which is only an extention of your datascroller, will delagate operations to a pagination interface. Some examples include a method that retrieves that page count, and another that set the active data list for the datagrid/datatable....

                the interface looks like the following:

                public int determinePageCount(PaginationConfig config);
                public List<BaseDomain> getActiveDataList();
                


                When the page is first loaded, my application will get data for page 1. Which I configured to only return rows 1 - 5. When the user clicks on page 2, the application again retrieved rows 6 - 10, and set the activeDateList to contain that data. However, richfaces datagrid is not displaying the second page worth of data...

                Here is some more code...

                My datagrid...
                <rich:dataGrid id="commentGrid" value="#{ParkApi.parkCommentPaginator.activeDataList}" var="parkComment" columns="1" elements="5">
                

                My custom scroller...
                <ltp:ltpDataScroller scrollerListener="#{ParkApi.commentScrollerListener.processScroller}" for="commentGrid" paginator="#{ParkApi.parkCommentPaginator}" rendered="#{fn:length(ParkApi.createdPark.commentList) gt 0}"/>
                


                My Datascroller listener class...

                public class GenericScrollerListener implements DataScrollerListener {
                 protected static Logger LOG = Logger.getLogger(GenericScrollerListener.class);
                 private Paginator paginator;
                
                 public GenericScrollerListener(Paginator paginator){
                 this.paginator = paginator;
                 }
                 public void processScroller(DataScrollerEvent event) {
                 LOG.debug("Processing scroll event, retrieving page data for "+event.getPage());
                
                 List data = paginator.getDataListForPage(event.getPage(), PaginationConfig.DEFAULT_CONFIG);
                 paginator.setActiveDataList(data);
                
                 ((LtpDataScroller)event.getSource()).setPage(event.getPage());
                 }
                 public Paginator getPaginator() {
                 return paginator;
                 }
                 public void setPaginator(Paginator paginator) {
                 this.paginator = paginator;
                 }
                
                
                
                }
                


                Why won't the datagrid refresh with the newly fetched data...is there a need for the equivalent of a reRender attribute??