问题描述
我在延迟加载 Primefaces Datascroller时遇到问题组件.
我有一个jsf页面,该页面应在页面加载时显示10个事件.如果用户希望查看更多内容,则可以单击更多按钮以加载并显示10个下一个事件.对于每个事件行,都有一个可用于显示事件详细信息的链接.
I have a jsf page that should display 10 events on page load. If the user wants to see more he/she can click the more button to load and display the 10 next events. For each of the event rows, there is a link that can be used to display the event's details.
<h:form id="mainForm" >
<p:dataScroller value="#{backing.lazyModel}" var="event" lazy="true" chunkSize="10" rowIndexVar="index">
#{event.name}
<p:commandLink class="view-trigger"
value="View Event Details"
actionListener="#{backing.initViewEventDetails(index, event)}"/>
<f:facet name="loader">
<p:outputPanel
visible="#{backing.lazyModel.rowCount gt 10}"
rendered="#{backing.lazyModel.rowCount gt 10}">
<p:commandLink value="More" />
</p:outputPanel>
</f:facet>
</p:dataScroller>
</h:form>
初始搜索工作正常,也就是说,当我单击查看事件详细信息"链接时,将调用后备bean,并且看到接收到的索引和事件与我单击的行相对应.
The initial search works fine, that is, when I click the view event details link, my backing bean is invoked and I see that the index and event received correspond to the row I clicked on.
但是,一旦加载下一个包含1个额外事件的块,页面将显示11个事件,但是单击查看事件详细信息"链接将发送正确的索引,但不会发送正确的事件.例如,如果我单击索引0处的事件,就会得到索引10处的事件,如果我单击索引1处的事件,那么不会调用我的支持bean.
However, once I load the next chunk, which consists of 1 extra event, the page displays 11 events but clicking a view event details link sends the proper index but does not send the proper event. For example, if I click on event at index 0, I get the event at index 10, if I click on event at index 1 my backing bean is not invoked.
当我单击更多"按钮时,数据滚动器似乎忘记了最近的10个事件,但是我的惰性数据模型仍然记得.
It looks like the datascroller forgets about the last 10 events when I click on the more button but my lazy data model still remembers.
后备豆:
@ManagedBean(name="backing")
@ViewScoped
public class DataScrollerBacking implements Serializable {
private static final long serialVersionUID = 4012320411042043677L;
private static final Logger LOGGER = Logger.getLogger(DataScrollerBacking.class);
@ManagedProperty("#{settings.dataSource}")
private String dataSource;
private WebEventDAO webEventDAO;
private LazyDataModel<Event> lazyModel;
@PostConstruct
public void init() {
webEventDAO = CommonDAOFactory.getInstance(dataSource).getWebEventDAO();
search();
}
public void search() {
DateTime start = new DateTime(2014, 1, 1, 0, 0 ,0);
final Date startDate = start.toDate();
final Date endDate = start.plus(Years.ONE.toPeriod()).minus(Seconds.ONE.toPeriod()).toDate();
lazyModel = new LazyDataModel<Event>() {
private static final long serialVersionUID = 1231902031619933635L;
private LinkedHashSet<Event> eventCache; // Ordered set of all retrieved events so far.
// I'm using a set because the load method is called twice on page load (any idea why???) and I don't want duplicates in my cache.
@Override
public List<Event> load(int first, int pageSize, String sortField, SortOrder sortOrder, Map<String, Object> filters) {
List<Event> events = new ArrayList<Event>(10);
try {
if(eventCache == null){
int count = webEventDAO.getSearchByPeriodRaceTypeAndRaceStatusForCompanyCount(Collections.singletonList(1), startDate, endDate, null, null);
this.setRowCount(count);
eventCache = new LinkedHashSet<Event>(count);
}
events = webEventDAO.searchByPeriodRaceTypeAndRaceStatusForCompany(Collections.singletonList(1), startDate, endDate, null, null, true, first, pageSize);
eventCache.addAll(events);
} catch (DAOException e) {
LOGGER.error("An error occurred while retrieving events.", e);
}
return events;
}
};
}
public void initViewEventDetails(Integer index, Event event){
LOGGER.info("index=" + index + " eventname=" + event.getName());
}
public String getDataSource() {
return dataSource;
}
public void setDataSource(String dataSource) {
this.dataSource = dataSource;
}
public LazyDataModel<Event> getLazyModel() {
return lazyModel;
}
public void setLazyModel(LazyDataModel<Event> lazyModel) {
this.lazyModel = lazyModel;
}}
由于该页面显示正确的信息,并且接收到的索引始终有效,所以我当前的解决方法是按索引在惰性数据模型中获取事件.
Since the page displays the proper information and the index received is always valid, my current workaround is to go fetch the Event in the lazy data model by index.
但是,我想了解为什么收到的事件不是我单击的事件.
However, I would like to understand why the received event is not the one I clicked on.
我做错什么了吗,或者这只是滚动条的实现方式?
Am I doing something wrong or this is just how the scroller is implemented?
在Mojarra 2.2,Tomcat 7,Primefaces 5,Omnifaces 1.8上运行
Running on Mojarra 2.2, Tomcat 7, Primefaces 5, Omnifaces 1.8
推荐答案
我终于有时间花在这个问题上,并且找到了解决方法.这是一种黑客行为,所以也许正确的解决方案是使用其他组件或创建自己的组件.
I finally had time to spend on this issue and I found a workaround. It's a hack so maybe the proper solution would be to use a different component or create my own.
当将DataScroller与LazyDataModel一起使用时,似乎出现了Primefaces DataScroller限制.似乎该组件并非旨在执行此操作.
It seems like Primefaces DataScroller limitation that occurs when using the DataScroller with a LazyDataModel. It would seem that the component was not designed to do this.
为避免此问题,我实现了自己的延迟加载,除了新添加的元素外,还返回了相同的列表实例.
To avoid this issue, I implemented my own lazy loading where the same list instance is returned in addition to the newly added elements.
这是我先前的示例,经过修改以实现这种新的延迟加载模式:
Here is my previous example modified to implement this new lazy loading pattern:
html页面:
<h:form id="mainForm" >
<p:dataScroller value="#{backing.events}" var="event" rowIndexVar="index">
#{event.name}
<p:commandLink class="view-trigger"
value="View Event Details"
action="#{backing.initViewEventDetails(index, event)}"/>
<f:facet name="loader"><h:outputText value=""/></f:facet>
</p:dataScroller>
<p:commandLink value="More" process="@form" update="@form"
action="#{backing.loadMore()}"
visible="#{backing.totalCount gt backing.events.size()}"
rendered="#{backing.totalCount gt backing.events.size()}"/>
</h:form>
DataScroller不再具有lazy ="true",chunkSize ="10",使用名为events的列表作为值,并声明一个空的加载器构面(避免在到达列表底部时自动加载更多内容) .我使用了一个commandback,它调用backing.loadMore()并更新表单以替换加载程序构面.
The DataScroller no longer has lazy="true", chunkSize="10", uses a list called events as the value and declares an empty loader facet (to avoid auto-load more when the bottom of the list is reached). I used a commandLink that calls backing.loadMore() and updates the form to replace the loader facet.
后备豆:
@Named("backing")
@ViewScoped
public class DataScrollerBacking implements Serializable {
private static final long serialVersionUID = 4012320411042043677L;
private static final Logger LOGGER = Logger.getLogger(DataScrollerBacking.class);
private static final Integer CHUNK_SIZE = 10;
@DataSource
@Inject
private String dataSource;
private WebEventDAO webEventDAO;
private List<Event> events;
private Integer totalCount;
private Date startDate;
private Date endDate;
@PostConstruct
public void init() {
webEventDAO = CommonDAOFactory.getInstance(dataSource).getWebEventDAO();
search();
}
public void search() {
DateTime start = new DateTime(2014, 1, 1, 0, 0 ,0);
startDate = start.toDate();
endDate = start.plus(Years.ONE.toPeriod()).minus(Seconds.ONE.toPeriod()).toDate();
try {
totalCount = webEventDAO.getSearchByPeriodRaceTypeAndRaceStatusForCompanyCount(Collections.singletonList(1), startDate, endDate, null, null);
events = new ArrayList<Event>(totalCount);
loadMore();
} catch (DAOException e) {
LOGGER.error("An error occurred while retrieving events.", e);
}
}
public void loadMore() {
List<Event> newEvents = new ArrayList<Event>(CHUNK_SIZE);
try {
newEvents = webEventDAO.searchByPeriodRaceTypeAndRaceStatusForCompany(Collections.singletonList(1), startDate, endDate, null, null, true, events.size(), CHUNK_SIZE);
events.addAll(newEvents);
} catch (DAOException e) {
LOGGER.error("An error occurred while retrieving events.", e);
}
}
public void initViewEventDetails(Integer index, Event event){
LOGGER.info("index=" + index + " eventname=" + event.getName());
}
public String getDataSource() {
return dataSource;
}
public void setDataSource(String dataSource) {
this.dataSource = dataSource;
}
public List<Event> getEvents() {
return events;
}
public void setEvents(List<Event> events) {
this.events = events;
}
public Integer getTotalCount() {
return totalCount;
}
public void setTotalCount(Integer totalCount) {
this.totalCount = totalCount;
}}
在后备bean中,搜索方法对事件总数进行计数,保存该信息,然后调用loadMore()将事件列表中的前10个事件加载.
In the backing bean, the search method counts the total number of events, saves that information and calls loadMore() to load the first 10 events in the events list.
单击更多按钮时,再次调用loadMore(),然后在事件列表的末尾附加下10个事件.
When the more button is clicked, loadMore() is called again and the next 10 events are appended at the end of events list.
现在,当我单击新加载的元素时,commandLink会使用正确的值调用后备bean.
Now when I click on newly loaded elements, the commandLink invokes the backing bean with the correct value.
这篇关于CommandLink在延迟加载的Primefaces Datascroller上不起作用的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!