Timeline supports lazy loading of events during moving / zooming in the timeline. This makes sense when event's loading is time-consuming. Events are loaded lazy when p:ajax with event="lazyload" is attached to the p:timeline tag. The event class TimelineLazyLoadEvent contains one or two time ranges the events should be loaded for (two times ranges occur when you zoom out the timeline).
Note: The "lazyload" listener is not invoked again when the visible time range (incl. some hidden ranges defined by preloadFactor) already has lazy loaded events.
<div class="card">
<h:form id="form">
<p:growl id="growl" showSummary="true" showDetail="false">
<p:autoUpdate/>
</p:growl>
<div id="loadingText" style="font-weight:bold; margin:-5px 0 5px 0; visibility:hidden;">Loading ...
</div>
<p:timeline id="timeline" value="#{lazyTimelineView.model}"
preloadFactor="#{lazyTimelineView.preloadFactor}"
zoomMax="#{lazyTimelineView.zoomMax}" minHeight="170">
<p:ajax event="lazyload" update="@none" listener="#{lazyTimelineView.onLazyLoad}"
onstart="$('#loadingText').css('visibility', 'visible')"
oncomplete="$('#loadingText').css('visibility', 'hidden')"/>
</p:timeline>
<h:panelGrid columns="2" style="margin-top:15px">
<p:spinner id="spinner" value="#{lazyTimelineView.preloadFactor}"
min="0" max="1" stepFactor="0.05"/>
<p:commandButton value="Update Preload Factor" process="@this spinner" update="timeline"
action="#{lazyTimelineView.clearTimeline}"
style="margin-left:5px"/>
</h:panelGrid>
</h:form>
</div>
@Named
@ViewScoped
public class LazyTimelineView implements Serializable {
private TimelineModel<String, ?> model;
private float preloadFactor = 0;
private long zoomMax;
@PostConstruct
protected void initialize() {
// create empty model
model = new TimelineModel<>();
// about five months in milliseconds for zoomMax
// this can help to avoid a long loading of events when zooming out to wide time ranges
zoomMax = 1000L * 60 * 60 * 24 * 31 * 5;
}
public TimelineModel<String, ?> getModel() {
return model;
}
public void onLazyLoad(TimelineLazyLoadEvent e) {
try {
// simulate time-consuming loading before adding new events
Thread.sleep((long) (1000 * Math.random() + 100));
}
catch (InterruptedException ex) {
// ignore
}
TimelineUpdater timelineUpdater = TimelineUpdater.getCurrentInstance(":form:timeline");
LocalDateTime startDate = e.getStartDateFirst(); // alias getStartDate() can be used too
LocalDateTime endDate = e.getEndDateFirst(); // alias getEndDate() can be used too
// fetch events for the first time range
generateRandomEvents(startDate, endDate, timelineUpdater);
if (e.hasTwoRanges()) {
// zooming out ==> fetch events for the second time range
generateRandomEvents(e.getStartDateSecond(), e.getEndDateSecond(), timelineUpdater);
}
}
private void generateRandomEvents(LocalDateTime startDate, LocalDateTime endDate, TimelineUpdater timelineUpdater) {
LocalDateTime curDate = startDate;
Random rnd = new Random();
PrimitiveIterator.OfInt randomInts = rnd.ints(1, 99999).iterator();
while (curDate.isBefore(endDate)) {
// create events in the given time range
if (rnd.nextBoolean()) {
// event with only one date
model.add(TimelineEvent.<String>builder()
.data("Event " + randomInts.nextInt())
.startDate(curDate)
.build(), timelineUpdater);
}
else {
// event with start and end dates
model.add(TimelineEvent.<String>builder()
.data("Event " + randomInts.nextInt())
.startDate(curDate)
.endDate(curDate.plusHours(18))
.build(),
timelineUpdater);
}
curDate = curDate.plusHours(24);
}
}
public void clearTimeline() {
// clear Timeline, so that it can be loaded again with a new preload factor
model.clear();
}
public void setPreloadFactor(float preloadFactor) {
this.preloadFactor = preloadFactor;
}
public float getPreloadFactor() {
return preloadFactor;
}
public long getZoomMax() {
return zoomMax;
}
}