Adwin
Adwin

Reputation: 105

How to do pagination in JSF 2

I am very new to JSF and was trying to implement a pagination using <ui:repeat>. I am using the @ViewScoped but currently my state is not saved. Every time the backing bean resets the page information and the next page is not displayed. Attaching the source code for reference.

WorkItemList.xhtml

<?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:composition xmlns="http://www.w3.org/1999/xhtml"
    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:c="http://java.sun.com/jsp/jstl/core"
    template="/WEB-INF/template/default.xhtml">

<ui:define name="content">
    <h:form id="form">

          <ui:repeat value="#{repeatPaginator.model}" var="listItem">
            <div>
                <h:outputText value="#{listItem}"/>
            </div>
        </ui:repeat>
        <h:commandButton value="&lt; prev" action="#{repeatPaginator.prev}"/>
        <h:outputText value="#{repeatPaginator.pageIndex} / #{repeatPaginator.pages}"/>
        <h:commandButton value="next &gt;" action="#{repeatPaginator.next}"/>
        <h:inputHidden value="#{repeatPaginator.pageIndex}"/>
    </h:form>
</ui:define>
</ui:composition>

Backing Bean - RepeatPaginator.java

package test;
@ManagedBean
@ViewScoped
public class RepeatPaginator implements Serializable{

    private static final long serialVersionUID = 1L;
    private static final int DEFAULT_RECORDS_NUMBER = 2;
    private static final int DEFAULT_PAGE_INDEX = 1;
    private TestDelegate delegate;
    private int records;
    private int recordsTotal;
    private int pageIndex;
    private int pages;
    private List<String> model;
    public RepeatPaginator() {
        delegate = new TestDelegate();
        this.records = DEFAULT_RECORDS_NUMBER;
        this.pageIndex = DEFAULT_PAGE_INDEX;
        // Get Model
        this.model = delegate.fetchCurrentList(getFirst(), getFirst()+records);
        this.recordsTotal = delegate.getListSize();

        if (records > 0) {
            pages = records <= 0 ? 1 : recordsTotal / records;

            if (recordsTotal % records > 0) {
                pages++;
            }

            if (pages == 0) {
                pages = 1;
            }
        } else {
            records = 1;
            pages = 1;
        }

        updateModel();
    }

    public void updateModel() {
        int fromIndex = getFirst();
        int toIndex = getFirst() + records;

        if(toIndex > this.recordsTotal) {
            toIndex = this.recordsTotal;
        }

         setModel(delegate.fetchCurrentList(fromIndex, toIndex));
    }

    public void next() {
        if(this.pageIndex < pages) {
            this.pageIndex++;
        }

        updateModel();
    }

    public void prev() {
        if(this.pageIndex > 1) {
            this.pageIndex--;
        }

        updateModel();
    }   

    public int getRecords() {
        return records;
    }

    public int getRecordsTotal() {
        return recordsTotal;
    }

    public int getPageIndex() {
        return pageIndex;
    }

    public int getPages() {
        return pages;
    }

    public int getFirst() {
        return (pageIndex * records) - records;
    }

    public List<String> getModel() {
        if(model==null)
            updateModel();
        return model;
    }

    public void setModel(List<String> model) {
        this.model = model;
    }

    public void setPageIndex(int pageIndex) {
        this.pageIndex = pageIndex;
    }
}

Delegate - TestDelegate.java

@ViewScoped
public class TestDelegate {

    private List<String> list = new ArrayList<String>();
    public TestDelegate()
    {
        this.list.add("Item 1");
        this.list.add("Item 2");
        this.list.add("Item 3");
        this.list.add("Item 4");
        this.list.add("Item 5");
        this.list.add("Item 6");
        this.list.add("Item 7");
        this.list.add("Item 8");
        this.list.add("Item 9");
        this.list.add("Item 10");
        this.list.add("Item 11");
    }

    public List<String> fetchCurrentList(int from, int to)
    {
        return list.subList(from, to);
    }

    public int getListSize()
    {
        return this.list.size();
    }
}

Upvotes: 2

Views: 7346

Answers (1)

dognose
dognose

Reputation: 20889

The @ViewScoped bean is not "magically present all the time". At the end of the request, it's values will be serialized and the bean is destructed.

This causes a Re-Construct of the bean at the beginning of the next request (click on next). Your Container will take care to deserialize the stored values again, in order to return the actual State of the bean. BUT: This happens after Bean-Construction, meaning when you try to use the values within the Constructor of the bean, nothing is restored.

Move your logic into a method that you annotate with @PostConstruct - then all values will be loaded and you can fetch the correct result. The container will call the method annotated with @PostConstruct once it restored the prior state of the bean.

This becomes important when you start to use ManagedProperties or CDI-Injections.

public RepeatPaginator() {
   //no serialized data available here, Good location to initialize some stuff 
   //that is independent.
   delegate = new TestDelegate();
   this.records = DEFAULT_RECORDS_NUMBER;
   this.pageIndex = DEFAULT_PAGE_INDEX;
}

@PostConstruct
public void init() {
        //Here the ViewScoped values are restored. We can start to use them.

        // Get Model
        this.model = delegate.fetchCurrentList(getFirst(), getFirst()+records);
        this.recordsTotal = delegate.getListSize();

        if (records > 0) {
            pages = records <= 0 ? 1 : recordsTotal / records;

            if (recordsTotal % records > 0) {
                pages++;
            }

            if (pages == 0) {
                pages = 1;
            }
        } else {
            records = 1;
            pages = 1;
        }

        updateModel();
    }

Upvotes: 2

Related Questions