Mindwin Remember Monica
Mindwin Remember Monica

Reputation: 1539

Reloading a <p:dataScroller> on a form update

Random Thought: I am hating this "lone wolf" behavior they coded into the dataScroller....


I am trying to implement a filter based on the user choice on a <p:selectOneMenu> that would reload the contents shown in a <p:dataScroller> from the ManagedBean based on the choice.

MB (EnglishNumberToWords) (random strings)

import java.util.*;
import se.answers.EnglishNumberToWords;
import java.security.SecureRandom;

@ManagedBean
@ViewScoped
public class bean {
private List<String> itens;
private Integer choice = 1; //initialize;
private LazyDataModel<String> model;
// getter setter

@PostConstruct
public void postConstruct() {
    int count = loadStringsFromElsewhere();
    model = new LazyModelImplmentation(this);
    model.setRowCount(count);
}

public Map<String, Integer> mapChoices() {
    Map<String, Integer> map = new LinkedHashMap<String, Integer>();
    for(int ii=0;ii<5;ii++) {
        map.put(ii, convertLessThanOneThousand(ii));
    }
}

public List<String> getChunk(int first, int pageSize) {
    SecureRandom random = new SecureRandom();
    int listSize = itens.size();
    int added = 0;
    int end = int+pageSize;
    while(end > itens.size(){
        added++; //the real code here is different, I will just randomize.
        int criteria = (random.nextInt(5) + 1);
        if(criteria == choice) { // filters out Strings.
            String ss = criteria + BigInteger(130, random).toString(32)
            itens.add(ss);
        }
    }
    return itens.subList(Math.min(first, itens.size()), Math.min(end, itens.size()));
    }

/**
 *  Get the dataScroller itens from elsewhere, NOT a database.<p>
 *  here we will use only randons.
 */
private int loadStringsFromElsewhere() {
    SecureRandom random = new SecureRandom();
    if(itens == null) {
       itens = new ArrayList<String>();
    }
    for(int ii=0;ii<  (random.nextInt(50) + 100); ii++) {
        int criteria = (random.nextInt(5) + 1);
        String ss = criteria + BigInteger(130, random).toString(32);
        itens.add(ss);
    }
}
}

LazyModelImpl

import java.util.List;
import java.util.Map;

import org.primefaces.model.LazyDataModel;
import org.primefaces.model.SortOrder;

public class LazyModelImplmentation extends LazyDataModel<String> {
    private static final long serialVersionUID = 1L;
    private Bean bean;

public LazyModelImplmentation(Bean bean) {
    this.bean = bean;
}

@Override
    public List<String> load(int first, int pageSize, String sortField,
            SortOrder sortOrder, Map<String, Object> filters) {
        return bean.getChunk(first, pageSize);
    }   
}

JSF

<h:form prependId="false">
   <p:selectOneMenu value="#{bean.choice}">
        <f:selectItems value="#{bean.mapChoices()}" />
        <p:ajax process="@form" update="@form" />
    </p:selectOneMenu> 
    <p:dataScroller id="da_scroller" var="item" 
        value="#{bean.model}" rowIndexVar="index" chunkSize="10" lazy="true">

    <!-- SHOW THE DATA IN THE item -->
        <h:outputText value="#{index}: #{item.toString()}" />
        <hr />

    </p:dataScroller>
</h:form>

But the dataScroller just ignores the form update and keeps showing the same data. Only the new data loaded via the lazy model is updated, mixed with the old data.

How can I clean up the dataScroller on the form update so it displays only the new data (bonus points if it goes back to the first chunk).

Using Primefaces 5.0 on Tomcat 7 and jsf2.2 (but the jsf is on the tagging).

Upvotes: 3

Views: 1461

Answers (2)

Vitor Mendes Costa
Vitor Mendes Costa

Reputation: 21

There is an easy way:
Just put the datascroller inside a panel, then:

  • change the rendered atribute to false in the datascroller
  • update the panel
  • reload the datascroller list with new data
  • change the rendered atribute to true again in the datascroller
  • update the panel

= )

Upvotes: 0

Mindwin Remember Monica
Mindwin Remember Monica

Reputation: 1539

After fiddling around with the source for the <p:dataScroller> I came up with no solution. There is no documented way to change what was already appended, and the component just appends more stuff.

So I had to hack my own solution:

  1. Lie to the <p:dataScroller>:

    • The component does not work properly if you do not setRowCount() on the lazyModel. It only fetches two chunks and then stop.
    • Changing the rowCount on the fly also does not have the intended effect. The component keeps its own internal count. [3]
    • Also as of Primefaces 5.0, setting rowCount to Integer.MAX_VALUE causes the dataScroller to halt (client-side) on the fetch of the second chunk. I suspect some Shlemiel the painter [1] [2] somewhere.
    • So on the init of the LazyDataModel, set a rowCount large enough (but not too large). I set it to 100,000.
  2. Cheat: I have control of what is going to the dataScroller because I build the chunk on the @ManagedBean, so If I want to reset the list and start serving from the beginning, I can. I will leave the exact implementation of the getChunk() method (see the listing on the question, above) to the reader, but just keep your own count instead of relying on the params of LazyDataModel<T>.load().

  3. Steal: clean the already loaded entries on the AJAX call of the <p:selectOneMenu> (bind to the onstart, because you can't be sure to have a window to do it before the dataScroller updates itself:
<p:selectOneMenu value="#{bean.choice}">
    <f:selectItems value="#{bean.mapChoices()}" />
    <p:ajax process="@form" update="@form"  onstart="cleanScroller()" />
</p:selectOneMenu>
function cleanScroller() {
    $('li.ui-datascroller-item').remove();
}

Upvotes: 3

Related Questions