Youraf
Youraf

Reputation: 250

actionListener commandLink not working on lazy dataScroller

In a project i need to lazy load object from database, and for each element i will put a link to redirect to a specific page.

The lazy loading is working. and when a click on the link for the first element it's ok, the problem is after scrolling, the next element don't call the listner listOi.editer().

<p:dataScroller value="#{listOi.lazyOi}" var="oi" id="OisChoser" widgetVar="scroller" 
  chunkSize="5" mode="inline" scrollHeight="531" lazy="true" style="width: 597px;" rows="5" >
  <h:panelGroup id="info_OI" class="info_OI" align="center" >
   ...
   <h:commandLink actionListener="#{listOi.editer()}" immediate="true" >
     <f:param name="selectedoiId" value="#{oi.id}" />
     <span class="crayon" style='cursor: pointer;'></span>
   </h:commandLink>
   ...
  </h:panelGroup 
</p:dataScroller>

Upvotes: 3

Views: 1192

Answers (1)

kcdgkn
kcdgkn

Reputation: 116

The problem is that PrimeFaces' LazyDataModel does not keep a complete model of the data displayed in the view. It only keeps track of the most recently loaded items and discards older items. This makes that these items are no longer accessible from JSF.

But since you are subclassing that class anyway (it's abstract), it's pretty easy to alter that behavior. Basically what you want to do is keep track of all the data you loaded so far and return that data as asked for. At the minimum, you need to override setRowIndex(), getRowData(), isRowAvailable() and load(). Below is an example that works for me.

public class MyLazyModel extends LazyDataModel<SomeType> implements Serializable {

    private final List<SomeType> data;
    private int rowIndex;

    public MyLazyModel() {
        super();
        data = new ArrayList<>();
    }

    @Override
    public List<SomeType> load(int first, int pageSize, String sortField, SortOrder sortOrder, Map<String, Object> filters) {
        List<SomeType> retData;
        if (first >= data.size()) {
            retData = ... //Get the data from datasource
            data.addAll(retData);
            return retData;
        } else {
            return data.subList(first, Math.min(first + pageSize, data.size()));
        }
    }

    @Override
    public void setRowIndex(int index) {
        if (index >= data.size()) {
            index = -1;
        }
        this.rowIndex = index;
    }

    @Override
    public SomeType getRowData() {
        return data.get(rowIndex);
    }

    @Override
    public boolean isRowAvailable() {
        if (data == null) {
            return false;
        }
        return rowIndex >= 0 && rowIndex < data.size();
    }

}

Because you can never be sure that setWrappedData() (which is called after load() with the return data from that method) is called only once, you need to guard against adding doubles to your data model. Here I do this by only loading data that has never been loaded before and storing that data in the load() method, ignoring setWrappedData() completely. It's quite ugly and will lead to synchronization problems if your model is never invalidated, but it works. Anyway, you could circumvent this by always loading data and replacing old content with new, but that's not the core of the issue.

Also, because you now keep track of the data in the method itself, you need to override all methods in LazyDataModel that rely on LazyDataModel.data being correct (or at least that subset of them that you are using).

Final note: you of course have to make sure that your model returned to the JSF page is always the same, as discussed here.

Upvotes: 3

Related Questions