3

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).

Community
  • 1
  • 1
Mindwin Remember Monica
  • 1,469
  • 2
  • 20
  • 35
  • Updating the form in which the datascroller resides is, as it seems, not a 'reset' trigger. Check the javascript source of the datascroller to see if it contains some hidden functions to achieve this. Otherwise see if the java source contains something that you can use via binding – Kukeltje Jul 06 '15 at 21:09
  • Thanks. If you make this into a kind of `reset()` method, you can maybe submit it as a patch/pullrequest to the PF github – Kukeltje Jul 07 '15 at 13:51
  • @Kukeltje Submiting as a patch may be too advanced for me. And historically the PF guys have their own roadmap anyways. I will try to document my solution the best I can, though. – Mindwin Remember Monica Jul 07 '15 at 13:53
  • I know… but things have changed a little recently with the move to github. At least for bugfixes. – Kukeltje Jul 07 '15 at 13:54
  • @Kukeltje answer posted. I will delete some obsolete comments here, suggest you do the same. – Mindwin Remember Monica Jul 07 '15 at 15:05
  • Done, but keep in mind. Jsf 2.2 is not an implementation and version, it is an api and version. Mojarra/MyFaces 2.2.x is what should be posted – Kukeltje Jul 07 '15 at 15:09

2 Answers2

3

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();
}
Community
  • 1
  • 1
Mindwin Remember Monica
  • 1,469
  • 2
  • 20
  • 35
0

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

= )

zx485
  • 28,498
  • 28
  • 50
  • 59