1
votes

I'm having a hard time doing basic AJAX updates of a timeline.

Let me start with a basic example where I want to update the start and end times of a timeline based on the selection of a dropdown list:

<h:form id="form">
            <h:outputLabel for="period" value="#{str.schedule_period}"/>
            <h:selectOneMenu id="period" value="#{timelineController.period}" label="#{str.schedule_period}">
                <f:selectItems value="#{timelineController.periodWeeks}" />
                <p:ajax event="change" update="timeline" />
            </h:selectOneMenu>
            <pe:timeline id="timeline" value="#{timelineController.model}"
                         editable="true" 
                         eventMargin="0"
                         minHeight="120"
                         stackEvents="false"
                         start="#{timelineController.timelineStart}"
                         min="#{timelineControllertimelineStart}"
                         end="#{timelineController.timelineEnd}"
                         max="#{timelineController.timelineEnd}"
                         showNavigation="false" showButtonNew="false"
                         showCurrentTime="false"
                         axisOnTop="true"
                         timeZone="#{timelineController.timezone}"
                         zoomMin="28800000" 
                         dropActiveStyleClass="ui-state-highlight" dropHoverStyleClass="ui-state-hover">
                <p:ajax event="drop" listener="#{timelineController.onDrop}"
                        global="false" process="timeline"/>  
            </pe:timeline>
</h:form>

When I select an item in the dropdown list, an AJAX event fires and sets the period property in the backing bean, but the new value is not reflected in the timeline component. As a workaround, I wrapped the timeline in a p:outputPanel and updated the wrapper instead and it works:

...
<h:selectOneMenu id="period" value="#{timelineController.period}" label="#{str.schedule_period}">
                <f:selectItems value="#{timelineController.periodWeeks}" />
                <p:ajax event="change" update="wrapper" />
</h:selectOneMenu>
...
<p:outputPanel id="wrapper">
   <pe:timeline id="timeline" value="#{timelineController.model}"
                         editable="true" 
                         eventMargin="0"
                         minHeight="120"
                         stackEvents="false"
                         start="#{timelineController.timelineStart}"
                         min="#{timelineControllertimelineStart}"
                         end="#{timelineController.timelineEnd}"
                         max="#{timelineController.timelineEnd}"
                         showNavigation="false" showButtonNew="false"
                         showCurrentTime="false"
                         axisOnTop="true"
                         timeZone="#{timelineController.timezone}"
                         zoomMin="28800000" 
                         dropActiveStyleClass="ui-state-highlight" dropHoverStyleClass="ui-state-hover">
                <p:ajax event="drop" listener="#{timelineController.onDrop}"
                        global="false" process="wrapper"/>  
            </pe:timeline>
</p:outputPanel>

Note that I also had to change the process attribute of p:ajax to wrapper.

So my first question is: why doesn't the update work without wrapping the timeline component?

My second question is about drag and drop. As you can you see from my code above, I have attached a drop listener to the timeline. And I'm also able to drag and drop events from a p:dataList BEFORE I make a selection in the dropdown list. Once I select a new period in the dropdown list, the timeline gets updated appropriately, but I'm not able to drag and drop events to the timeline any more (the onDrop listener doesn't get fired). Here's my p:dataList:

<p:dataList id="eventsList" value="#{timelineController.users}"
                        var="user" itemType="none">  
                <h:panelGroup id="eventBox" layout="box" style="z-index:9999; cursor:move;">  
                    #{user.toString()}  
                </h:panelGroup>  
                <p:draggable for="eventBox" revert="true" helper="clone" cursor="move"/>  
</p:dataList> 

Any ideas what's wrong here?

I'm also including the TimelineController class for reference:

@ManagedBean
@ViewScoped
public class TimelineController {
    @EJB UserService userDao;

    private TimelineModel model;
    private String name;
    private ZoneId timezone;
    private Period period;
    private Duration defaultShiftDuration;
    private LocalDateTime timelineStart;
    private LocalDateTime timelineEnd;



    @PostConstruct
    protected void initialize() {
        timezone = ZoneId.of("Europe/Berlin);
        period = Period.ofWeeks(2);
        defaultShiftDuration = Duration.ofHours(8);
        timelineStart = LocalDateTime.now().with(DayOfWeek.MONDAY).withHour(0).withMinute(0).truncatedTo(ChronoUnit.MINUTES);

        // create timeline model
        model = new TimelineModel();

    }

    public TimelineModel getModel() {
        return model;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getTimezone() {
        return timezone.getId();
    }

    public void setTimezone(String timezone) {
        this.timezone = ZoneId.of(timezone);
    } 

    public List<SelectItem> getPeriodWeeks() {
        List<SelectItem> weeks = Lists.newArrayList();
        weeks.add(new SelectItem(1, "1 " + JsfUtil.getStringResource("schedule_week")));
        weeks.add(new SelectItem(2, "2 " + JsfUtil.getStringResource("schedule_weeks")));
        weeks.add(new SelectItem(3, "3 " + JsfUtil.getStringResource("schedule_weeks")));
        return weeks;
    }



    public int getPeriod() {
        return period.getDays() / 7;
    }

    public void setPeriod(int nWeeks) {
        this.period = Period.ofWeeks(nWeeks);
        timelineEnd = null;
    }

    public Date getTimelineStart() {
        return Date.from(timelineStart.atZone(timezone).toInstant());
    }

    public Date getTimelineEnd() {
        if (timelineEnd == null) {
            timelineEnd = timelineStart.plus(period);
        }
        return Date.from(timelineEnd.atZone(timezone).toInstant());
    }

    public void setStartsOn(String startsOn) {
        timelineStart = LocalDateTime.parse(startsOn + "T00:00");
        timelineEnd = null;
    }


    public List<User> getUsers(){
        return userDao.findAll();
    }

    public void onDrop(TimelineDragDropEvent e) {  
        // get dragged model object (event class) if draggable item is within a data iteration component,  
        // update event's start and end dates.  
        User user = (User) e.getData();  

        Date endDate =  Date.from(e.getStartDate().toInstant().plus(defaultShiftDuration));
        // create a timeline event (not editable)  
        TimelineEvent event = new TimelineEvent(user, e.getStartDate(), endDate, true, e.getGroup());  

        // add a new event  
        TimelineUpdater timelineUpdater = TimelineUpdater.getCurrentInstance(":form:timeline");  
        model.add(event, timelineUpdater);  

    }
 }
1

1 Answers

2
votes

The problem was a missing widgetVar attribute in the timeline component. This looks like a bug to me, since I'm not using the client side API of the component. I will file a bug in PF Extensions project.