gwtbootstrap3 / gwtbootstrap3-extras

Extra (third party) wrappers for GwtBootstrap3
Apache License 2.0
43 stars 89 forks source link

FullCalendar Event getStart() Exception #95

Open JasonB42 opened 9 years ago

JasonB42 commented 9 years ago

While adding events to the FullCalendar extra, the org.gwtbootstrap3.extras.fullcalendar.client.ui.Event.getStart() consistently throws a JavaScriptException saying that "theInstance[1507516].start.toDate is not a function"

To test this, I have created a clean GWT Project and added the following lines:

    Event event = new Event("id", "Event title");
    event.setStart(new Date());
    Window.alert("Event start: "+event.getStart());

(In file com.fullcalendartest.client.FullCalendarTest.java:113-115)

Which throws:

com.google.gwt.event.shared.UmbrellaException: Exception caught: (TypeError) @org.gwtbootstrap3.extras.fullcalendar.client.ui.Event::getStart()([]): theInstance[1507516].start.toDate is not a function

I have packaged the clean eclipse project (uses standard Dev plugin via old firefox instance): https://dl.dropboxusercontent.com/u/424887/FullCalendarTest.tar.bz2

I will work around the problem for now and look forward to learning more about JSNI so that I can fix these types of problems in the future.

Thank you, Jason B.

Edit: I am using version 0.8.1

klimeryk commented 9 years ago

Scratch that (that = my PR). It seems FullCalendar is doing some magic with the Event object:

start The date/time an event begins. Required. A Moment-ish input, like an ISO8601 string. Throughout the API this will become a real Moment object.

So, at some point, the toDate call will be legitimate, because start will become a Moment object. But in a simple, "detached" case like JasonB42 provided, it will just be a string and the getter will fail.

The getter would have to take that into account with something like:

var theInstance = this;
if (theInstance.@org.gwtbootstrap3.extras.fullcalendar.client.ui.Event::event.start
    if (typeof theInstance.@org.gwtbootstrap3.extras.fullcalendar.client.ui.Event::event.start.toDate === 'function') {
        return theInstance.@org.gwtbootstrap3.extras.fullcalendar.client.ui.Event::event.start.toDate();
    } else {
       return new Date(theInstance.@org.gwtbootstrap3.extras.fullcalendar.client.ui.Event::event.start);
    }
}
return null;

But then the same has to be done for getEndDate, getISOStart, getISOEnd. Is it worth it? The way I see it (from a short glance at the documentation and code), Event is passed to FullCalendar, it does it's magic, and when it comes back (for example, as part of an click event), getStart and friends should be working fine.

Baaaah, the style this class (and the rest of FullCalendar) is written is driving me mad - Event should be a JSO, plain and simple. No lenghty theInstance.@org.gwtbootstrap3.extras.fullcalendar.client.ui.Event::event.something, just this.something. But that would require rewriting all those classes that implement IsJavaScriptObject to have some consistency in the code. And that's a lot of work - maybe if I actually used this extra I'd consider it ;)

JasonB42 commented 9 years ago

I solved this problem by keeping a copy of the calendar event data in an external Map, referenced by the ID you pass into each event on creation.

Edit: I realize that this is probably not necessary on callback, but I found it safer to just assume Event.getMethods won't work.

Map<String, CalData> calCache = new HashMap();

protected void loadEvents() {
    for (CalData eventData : events) {
        String uniqEventID = Document.get().createUniqueId();
        calCache.put(uniqEventID, eventData);
        Event calEvent = new Event(uniqEventID, "" + data.getSumm());
        calendar.addEvent(calEvent);
    }
}

ClickAndHoverConfig clickHover = new ClickAndHoverConfig(new ClickAndHoverEventCallback() {
    public void eventClick(JavaScriptObject calendarEvent, NativeEvent event, JavaScriptObject viewObject) {
        Event calEvent = new Event(calendarEvent);
        onCalendarEventClick(calEvent.getId());
    }
}

Also if anyone is having problems loading data into the calendar, it has to initialize past a certain point for .addEvent to work correctly. I wanted to wrap the calendar to automatically cache & delay calls to addEvent, and give the code to the community, but for some reason it only sometimes worked. Eventually I ran out of time and found that the the suggested way in the documentation seems to always work:

fc = new FullCalendar("uniqueCalendarID", ViewOption.month, calendarConfig, false);
fc.addLoadHandler(new LoadHandler() {
    @Override
    public void onLoad(LoadEvent event) {
        loadEvents(); // See above
    }
});