Building a shared calendar with Backbone.js and FullCalendar: A step-by-step tutorial

Building a shared calendar with Backbone.js and FullCalendar: A step-by-step tutorial

In a prior post, I explained how Backbone.js can be used to implement cascading select boxes. However, this was pretty much just a read-only affair, and these were relatively simple HTML elements. How does Backbone fare creating, updating and deleting data via a much more complex UI component?

I recently had the chance to build a shared calendar tool with Backbone and FullCalendar, a jQuery plugin for a full-sized calendar with drag-and-drop capabilities. I was quickly able to get them playing nicely with each other, and in this entry I’ll cover step-by-step what it took to get there.

Introducing FullCalendar

FullCalendar is a great plugin. The documentation is complete and useful, and it’s not that hard to get it going – considering everything that it is capable of. Here’s a picture of it in action:

So how do we get a screen like this up and running? Let’s assume that we’ve got a RESTful service such that if we do a GET to /events, we are returned a JSON array of objects representing events, where each event will contain properties recognized by FullCalendar, specifically:

  • A unique id
  • A string title
  • A start date, encoded in ISO8601 format (for example, ‘2011-08-12T09:55:03Z’)
  • An end date, encoded in ISO8601 format
  • A color, encoded in any of the CSS color formats (for example ‘Red’, or ‘#ff0000’)

We’ll assume that our server has been configured with some test data.

Now we can write the following HTML:

<html>
	<head>
		<link rel='stylesheet' type='text/css' href='stylesheets/fullcalendar.css'/>
		<link rel='stylesheet' type='text/css' href='stylesheets/application.css'/>

		<script type='text/javascript' src='javascripts/jquery-1.5.1.min.js'></script>
		<script type='text/javascript' src='javascripts/fullcalendar.min.js'></script>
		<script type='text/javascript' src='javascripts/underscore.js'></script>
		<script type='text/javascript' src='javascripts/backbone.js'></script>
		<script type='text/javascript' src='javascripts/application.js'></script>
	</head>
	<body>
		<div id='calendar'></div>
	</body>
</html>

And then the following application.js file:

$(function(){
    $('#calendar').fullCalendar({
        header: {
            left: 'prev,next today',
            center: 'title',
            right: 'month,basicWeek,basicDay',
            ignoreTimezone: false
        },
        selectable: true,
        selectHelper: true,
        editable: true,
        events: 'events'
    });
});

Running this will give us a calendar complete with events, which FullCalendar can load itself from the back-end. Even though we included the Backbone.js files, they’re not actually being used yet.

Note the setting of ignoreTimezone to false. If we don’t do this, FullCalendar will ignore any timezone information we’ve embedded in our ISO8601 dates.

Bringing in Backbone

Instead of having FullCalendar load the events for us, let’s have Backbone do it and then pass them to FullCalendar to render. This is going to require us to introduce a Backbone model, collection and view:

$(function(){
    var Event = Backbone.Model.extend();

    var Events = Backbone.Collection.extend({
        model: Event,
        url: 'events'
    });

    var EventsView = Backbone.View.extend({
        initialize: function(){
            _.bindAll(this);

            this.collection.bind('reset', this.addAll);
        },
        render: function() {
            this.el.fullCalendar({
                header: {
                    left: 'prev,next today',
                    center: 'title',
                    right: 'month,basicWeek,basicDay',
                    ignoreTimezone: false
                },
                selectable: true,
                selectHelper: true,
                editable: true
            });
        },
        addAll: function(){
            this.el.fullCalendar('addEventSource', this.collection.toJSON());
        }
    });

    var events = new Events();
    new EventsView({el: $("#calendar"), collection: events}).render();
    events.fetch();
});

All of the action happens at the end. We create an events collection, and then a view that’s going to mediate between the collection and the calendar element in the DOM. This view registers itself to receive ‘reset’ events from the collection.

Next, we render the view immediately, which pops the calendar on the page. At this stage however, the calendar will have nothing in it.

Finally, we call events.fetch(), which causes Backbone to fetch the events from our back-end service and populate the collection with them. The collection then triggers a ‘reset’ event, which is detected by the view. The view then adds the events to the calendar as an event source, which will automatically cause the events to be displayed on the calendar. Note that the Events collection is not what’s passed into the calendar – instead we convert it to a plain array of JSON objects. Things like the id, title etc will only be available on each Event model via the get() function, whereas FullCalendar expects to be able to access them directly as properties. Fortunately, Backbone provides us with the toJSON method to do this transformation for us.

Let’s start a dialog

FullCalendar lets us detect when a period of time has been selected on the calendar. Let’s detect that event, and utilize jQuery UI to pop-up a dialog box for filling in the event details. First we have to add to the HTML. I’ve highlighted the new lines:

<html>
	<head>
		<link rel='stylesheet' type='text/css' href='stylesheets/jquery-ui-1.8.13.custom.css'/>
		<link rel='stylesheet' type='text/css' href='stylesheets/fullcalendar.css'/>
		<link rel='stylesheet' type='text/css' href='stylesheets/application.css'/>

		<script type='text/javascript' src='javascripts/jquery-1.5.1.min.js'></script>
		<script type='text/javascript' src='javascripts/jquery-ui-1.8.13.custom.min.js'></script>
		<script type='text/javascript' src='javascripts/fullcalendar.min.js'></script>
		<script type='text/javascript' src='javascripts/underscore.js'></script>
		<script type='text/javascript' src='javascripts/backbone.js'></script>
		<script type='text/javascript' src='javascripts/application.js'></script>
	</head>
	<body>
		<div id='calendar'></div>

		<div id='eventDialog' class='dialog ui-helper-hidden'>
			<form>
				<div>
					<label>Title:</label>
					<input id='title' class="field" type="text"></input>
				</div>
				<div>
					<label>Color:</label>
					<input id='color' class="field" type="text"></input>
				</div>
			</form>
		</div>
	</body>
</html>

Note that the dialog is initially hidden. We’ll get the Javascript to render it when the user selects some date range on the calendar. We’ve already done this sort of thing with the FullCalendar component, so doing it with a jQuery UI dialog should be pretty straightforward. In the interests of brevity, I’m not going to include all of the code again, only those lines that have changed, plus some context:

    ...
    var EventsView = Backbone.View.extend({
        ...
        render: function() {
            this.el.fullCalendar({
                header: {
                    left: 'prev,next today',
                    center: 'title',
                    right: 'month,basicWeek,basicDay',
                    ignoreTimezone: false
                },
                selectable: true,
                selectHelper: true,
                editable: true,
                select: this.select
            });
        },
        ...
        select: function(startDate, endDate) {
            new EventView().render();
        },
    });

    var EventView = Backbone.View.extend({
        el: $('#eventDialog'),
        initialize: function() {
            _.bindAll(this);
        },
        render: function() {
            this.el.dialog({
                modal: true,
                title: 'New Event',
                buttons: {'Cancel': this.close}
            });

            return this;
        },
        close: function() {
            this.el.dialog('close');
        }
    });
    ...

Now when we select a date range, we see this:

I agree that having to enter a color as text is kind of poxy, but that’s not why we’re here. I’ll leave it as an exercise to you, dear reader, to improve that in the final version of the source code if you want.

Oh, and there’s one other thing: this code doesn’t actually save anything yet.

Creating Events

Getting to this point wasn’t too hard, but it’s certainly more code than we’d probably have if we didn’t use Backbone. So the question arises: This is better…how?

The advantage of Backbone comes in when we start putting some logic behind this UI. Let’s start with creating an event. Firstly, let’s assume that if we do a POST to /events with JSON representing an event, it’ll save that event and return it to us – including it’s newly-set id. With this service in place, we can now do the following:

    ...
    var EventsView = Backbone.View.extend({
        ...
        select: function(startDate, endDate) {
            var eventView = new EventView();
            eventView.collection = this.collection;
            eventView.model = new Event({start: startDate, end: endDate});
            eventView.render();
        }
    });

    var EventView = Backbone.View.extend({
        ...
        render: function() {
            this.el.dialog({
                modal: true,
                title: 'New Event',
                buttons: {'Ok': this.save, 'Cancel': this.close}
            });

            return this;
        },
        save: function() {
            this.model.set({'title': this.$('#title').val(), 'color': this.$('#color').val()});
            this.collection.create(this.model, {success: this.close});
        },
        ...
    });
    ...
});

So now when a user selects a date range, we create a new Event model object and set the start and end date on it, before passing it to dialog, along with the collection that we’re expecting to add the new model to. We’ve also added a ‘Ok’ button to the dialog. It’ll now look like this when we put some data in it:

When the user clicks the ‘Ok’ button, it populates the remaining fields of the model, saves it to the back-end, then adds it to the collection.

But there’s a problem here. The new event doesn’t appear on the screen. If we refresh the page it’ll show up, but we don’t want to have to do that. Instead, let’s have our EventsView register to receive notifications when a new model is added to the collection. When it detects this notification, it’ll render the new event into the calendar:

    ...
    var EventsView = Backbone.View.extend({
        initialize: function(){
            _.bindAll(this);

            this.collection.bind('reset', this.addAll);
            this.collection.bind('add', this.addOne);
        },
        ...
        addOne: function(event) {
            this.el.fullCalendar('renderEvent', event.toJSON());
        }
    });
    ...

So now, as soon as we click ‘OK’ and the booking is successfully saved to the back-end, it’ll appear in the calendar:

Awesome! We’ve got two views that are interacting, but only via a collection. Something else could add an event to the same collection, and the calendar would pick it up just the same. This is one of the strengths of Backbone.js.

Updating Events

Now let’s add some support for updating events. Let’s assume we can do a PUT request to /events/[id] (where [id] is the ID of the event that we want to update) where the payload is JSON containing the updated details for the event.

Now we can then extend the existing dialog to support both creation and editing:

    ...
    var EventsView = Backbone.View.extend({
        render: function() {
            this.el.fullCalendar({
                ...
                select: this.select,
                eventClick: this.eventClick
            });
        },
        ...
        eventClick: function(fcEvent) {
            this.eventView.model = this.collection.get(fcEvent.id);
            this.eventView.render();
        }
    });

    var EventView = Backbone.View.extend({
        ...
        render: function() {
            this.el.dialog({
                modal: true,
                title: (this.model.isNew() ? 'New' : 'Edit') + ' Event',
                buttons: {'Ok': this.save, 'Cancel': this.close},
                open: this.open
            });

            return this;
        },
        open: function() {
            this.$('#title').val(this.model.get('title'));
            this.$('#color').val(this.model.get('color'));
        },
        save: function() {
            this.model.set({'title': this.$('#title').val(), 'color': this.$('#color').val()});

            if (this.model.isNew()) {
                this.collection.create(this.model, {success: this.close});
            } else {
                this.model.save({}, {success: this.close});
            }
        },
        ...
    });

Note how:

  • We added the open function to set the title and color textfields. This has the additional benefit of clearing out the fields when creating a new event.
  • We use the Backbone.Model.isNew() method to determine whether we are editing or creating an event.

Now when we click on an event, a dialog will pop-up and we can edit the details of that event:

However, when we click ‘OK’, we’ll have the same problem we had earlier: the calendar won’t redisplay the new details. To fix this, we need to detect the change and update our UI. Fortunately, the collection will emit a ‘change’ event if one of its models gets changed. We can listen for this event and take action:

    ...
    var EventsView = Backbone.View.extend({
        initialize: function(){
            _.bindAll(this);

            this.collection.bind('reset', this.addAll);
            this.collection.bind('add', this.addOne);
            this.collection.bind('change', this.change);

            this.eventView = new EventView();
        },
        ...
        change: function(event) {
            var fcEvent = this.el.fullCalendar('clientEvents', event.get('id'))[0];
            fcEvent.title = event.get('title');
            fcEvent.color = event.get('color');
            this.el.fullCalendar('updateEvent', fcEvent);
        }
    });
    ...

Note that we have to lookup the underlying event object in the calendar in order to update it. Fortunately, FullCalendar makes this easy for us by providing the clientEvents method to look up events by ID.

Now that we’ve done this, our event will be updated in the calendar immediately:

It even updated the color for us! How good is that?

Moving and Resizing Events

Updating an event’s name and color is OK, but what’d be really cool is if we could move and resize events on the calendar. Turn’s out it’s surprisingly easy:

    ...
    var EventsView = Backbone.View.extend({
        ...
        render: function() {
            this.el.fullCalendar({
                ...
                eventClick: this.eventClick,
                eventDrop: this.eventDropOrResize,
                eventResize: this.eventDropOrResize
            });
        },
        ...
        eventDropOrResize: function(fcEvent) {
            this.collection.get(fcEvent.id).save({start: fcEvent.start, end: fcEvent.end});
        }
    });
    ...

Once again, we look the element up in the collection and update the appropriate details. It’s kind of hard to demonstrate with screenshots (I’d encourage you to get the source code if you’d like to take it for a spin yourself), but I can now, for example, move our updated booking back a week and change it to a four-day event:

What’s more, if I reload the page, the booking retains in its new location and length – the updates really have been saved to the back-end! Impressive, eh?

Deleting Events

Whilst we’re here, we might as well allow users to delete events. Let’s assume we can do a DELETE request to /events/[id], where [id] is the ID of the event that we want to delete. We can then add a ‘Delete’ button to the dialog box for editing an event. Sure, it’s not the prettiest UI in the world, but we’re not being paid for our good looks here, are we?

    ...
    var EventsView = Backbone.View.extend({
        initialize: function(){
            ...
            this.collection.bind('destroy', this.destroy);
            ...
        },
        ...
        destroy: function(event) {
            this.el.fullCalendar('removeEvents', event.id);
        }
    });

    var EventView = Backbone.View.extend({
        ...
        render: function() {
            var buttons = {'Ok': this.save};
            if (!this.model.isNew()) {
                _.extend(buttons, {'Delete': this.destroy});
            }
            _.extend(buttons, {'Cancel': this.close});

            this.el.dialog({
                modal: true,
                title: (this.model.isNew() ? 'New' : 'Edit') + ' Event',
                buttons: buttons,
                open: this.open
            });

            return this;
        },
        ...
        destroy: function() {
            this.model.destroy({success: this.close});
        }
    });
    ...

So now, when we click on an event, we see a ‘Delete’ button:

When we click the ‘Delete’ button, the event model is deleted on the back-end. It’s collection detects this and emits a ‘destroy’ event, which is picked up by the EventsView. It then removes the event from the calendar, which puts us back to where we started this whole post:

So now we’ve come full circle: creating, editing and – lastly – deleting an event.

Summing Up

In this post I’ve shown how Backbone.js can work quite nicely with sophisticated jQuery plugins like FullCalendar. We’ve also worked through the full lifecycle of creating, reading, updating and deleting model objects – a task that Backbone makes easy. Finally, we’ve seen how Backbone’s event-based approach can decouple models from views, which reduces spaghetti code.

In my opinion, frameworks like Backbone.js will have a strong role to play in the future of web development by proving that client-side Javascript development doesn’t have to be a structureless free-for-all.

The final version of the source code in this blog entry can be found here

ben.teese@shinesolutions.com

I'm a Senior Consultant at Shine Solutions.

61 Comments
  • akismet-a59efb2d5e8c0b994fb39e2d67a207be
    Posted at 20:52h, 10 August Reply

    Thanks, great tutorial. I’m also using Backbone and FullCalendar in a project at the moment, both tools work well together.

  • Benjamin Lowenstein
    Posted at 17:01h, 26 September Reply

    Found this very helpful, thank you very much. Also believe that the two are a power-combo, and this is a generally insightful example of how to cleanly integrate feature-packed jquery plugins with the structure of backbone.

  • PlanB
    Posted at 22:36h, 08 November Reply

    Very good tutorial, helped me a lot!
    But how could i store the events in a database like MySQL?
    Thats would be awesome for integrating that nice calendar in a webproject 🙂

    • Ben Teese
      Posted at 09:51h, 09 November Reply

      @PlanB The source code at https://github.com/shinetech/BackboneJsAndFullCalendar includes a Rails project for the back-end. This project is currently using sqlite3, but could be reconfigured to use MySQL by installing the appropriate gems and changing config/database.yml to point to your MySQL instance.

      • PlanB
        Posted at 01:36h, 10 November

        I have tryed to sort the things out but ruby isnt my language, js is still difficult enough for me to figure out how i can get it working, but thank you very much i will try to understand the project and ruby and mvc to figure it out 🙂

  • webdev
    Posted at 06:53h, 17 November Reply

    Popup window doesn’t work(chrome, ff)

  • Manuel Alzola
    Posted at 21:00h, 21 November Reply

    Hello, an awesome tutorial, the best one i’ve seen on backbone on par with your own ‘Cascading Select Boxes’

    I’m having a problem with it. In the first iteration, when you depend entirely on fullcalendar to fetch the json data, the service gets the start date and end date of the range selected in fullcalendar, but, since putting the responsability on the Backbone collection, that information is lost. How can I make it work again? Thank you very much for your wonderful work
    Manuel Alzola
    Madrid

    • al3via
      Posted at 22:06h, 27 May Reply

      HI Manuel.
      I was in the same question. Thanks for your comment. My problem was how to get /set recurring events. Which I think should be generated at server side dynamically. If I don´t send a period of time to server, there is a risk for an infinite loop for event which hasn´t an end date.

  • Manuel, Madrid
    Posted at 00:19h, 23 November Reply

    I’ve found the answer to my rather silly question. Here it is: Replace

    events.fetch();

    using instead:

    var view = $(‘#calendar’).fullCalendar(‘getView’);
    console.log(‘fetch start ‘ + view.start + ‘end ‘ + view.end);
    events.fetch({data: {start: Number(view.start), end: Number(view.end)}});

    • Sachin Gulaya
      Posted at 15:19h, 29 November Reply

      Hi Manuel,

      I think the issue relates to this code:

      select: function(startDate, endDate) {
      new EventView().render();
      }

      I replaced that code with:

      select: function(start, end, allDay) { // changed from startDate and endDate
      this.eventView.collection = this.collection;
      this.eventView.model = new Event({start: start, end: end, allDay: allDay}); // changed from startDate and endDate
      this.eventView.render();
      }

      By changing ‘startDate’ and ‘endDate’ to ‘start’ and ‘end’ I was able to get new events to fill the correct time slot on the calendar. If you look at the selectable.html example in the full calendar demo this is how full calendar suggests to do it.

  • Jinu Mathews
    Posted at 15:53h, 02 December Reply

    on clicking the ok button in the add event pop up no action is taking place. How to do it?

    • Victor
      Posted at 01:32h, 22 March Reply

      Jinu, did you receive an answer or were you able to figure a solution to your question? I’m having the exact same problem. The calendar loads fine but when I select one or a couple days I get the pop-up and fill out the fields fine. If I click on ‘Cancel’ the window closes but when I click on “Ok” absolutely nothing happens. The pop-up doesn’t even disappears, I have to click on ‘Cancel’.

      I created the files in my own folders for the project I’m working on and downloaded a working from “public” but I’m still having the same problem. I’m not a novice to web development but I am to javascript though, so any help or guidance will be very much appreciate. Thank you everyone!

      • Scott Mise
        Posted at 02:37h, 03 January

        I am also having this issue. Any solutions?

    • Dimitris Papageorgiou
      Posted at 01:49h, 21 March Reply

      Hi,did you find a solution to your problem?.I am facing the similar problem

  • Sachin Gulaya
    Posted at 07:36h, 31 December Reply

    Hi Ben, I found a bug in this implementation and I would appreciate your help in fixing it(I’ve spent ~8 hours tracking it down so far but I’m a novice in javascript).

    How to recreate it:
    Make a new event. Then use the forward arrow button in the top left corner to forward 5 weeks. Then go back 5 weeks using the back arrow. Your existing events will be there but your new event will not show up.

    If you go outside the scope of your defaultView(whether its agendaWeek, agendaDay, or month) the new event disappear until you refresh the page. The events that existed when you first loaded the calendar will still be there.

    So far it looks like a solution will involve having fullcalendar reload the events collection from backbone using the events option : http://arshaw.com/fullcalendar/docs/event_data/events_function/ or http://arshaw.com/fullcalendar/docs/event_data/events_json_feed/ .

    I’ve tried a few solutions(involving events.fetch() and events.toJSON() ) but am in over my head. Hopefully you can shine some light on this?

    • Sachin Gulaya
      Posted at 05:23h, 06 January Reply

      Hi Ben, any chance I can get some advice on how to fix this?

    • Sachin Gulaya
      Posted at 05:20h, 07 January Reply

      Finally solved it and it was ridiculously simple to do. Just needed to set the renderEvent [,stick] flag to true.

      addOne: function(event) {
      this.el.fullCalendar(‘renderEvent’, event.toJSON(), true);
      }

      http://arshaw.com/fullcalendar/docs/event_rendering/renderEvent/

  • swihart
    Posted at 10:26h, 31 March Reply

    Just getting started with your awesome tutorial, thanks so much for putting it together, it’s just what I needed.

    But one thing I had to hurdle over right at the start was how you call fullCalendar as this.el.fullCalendar. I had to make it this.$el.fullCalendar, otherwise it gave me an error (Uncaught TypeError: Object # has no method ‘fullCalendar’) because I was trying to call fullCalendar on a plain HTML element, rather than a jQuery object.

    I found this explained in the second paragraph here, apparently Backbone automagically creates a jQuery object of el called $el? : http://gordonkoo.com/blog/2012/03/06/backbones-hard-dependency-on-jquery.html

    I notice the ToDo example Backbone app is coded the way I had to make it, with an initial el: $(‘#todoapp’), later referred to as $el, which made me feel a little better that I wasn’t missing something: http://documentcloud.github.com/backbone/examples/todos/index.html

    Still I wonder how you got it to work without the dollar sign.

  • juan
    Posted at 01:08h, 11 April Reply

    Hi Ben, i have a error this.el.fullCalendar fullcalecar is not a function, but if use $(this.el).fullCalendar … works well, this is correct?,should replace all occurrences of this.el for $(this.el), I am newbie to javascript and I’m not sure if it is correct or not taking the correct object el:$ (“# calendar”) correctly,
    sorry for my English, Greetings from Argentina.
    Thank you very much, very good tutorial!

    • swihart
      Posted at 06:02h, 11 April Reply

      I had the same issue juan, I don’t know if this is due to a change in how Backbone.js works in a newer version or how else it was working for Ben, but I think it’s correct to use $(this.el) where you need to use a jQuery function (ie fullcalendar) on `el`.

      • Juan
        Posted at 08:38h, 11 April

        Tks swihart,I resolved to using the following :
        initialize: function(){
        _.bindAll(this);
        this.collection.bind(‘reset’, this.addAll);
        this.el=$(this.el);
        },
        I’m not sure if it’s the right way, but it works.
        Thank you very much!

  • juan
    Posted at 00:13h, 18 April Reply

    Hello, I have doubts on how to implement the binding between the previous and next buttons and the collection of fullcalendar and refetch the collection, and display these events in the fullcalendar, any suggestions, thank you very much!

  • swihart
    Posted at 02:28h, 18 April Reply

    juan — I think what you want to do is just load everything into the Collection and have no callback associated with the next / prev buttons. This way the months change instantly without waiting for the data to be sent.

    I load the initial month’s data quickly for a better user-experience and then subsequently load all the data (replacing the initial data). You could make the data loading more complex if needed.

    This is what I’m doing (in Coffeescript)..

    events fetch
    data:
    ‘start__gte’: {{ start }}
    ‘end__lt’: {{ end }}
    success:
    fetch()

    {{ start }} and {{ end }} are variables passed into the template from my server-side framework (Django in my case). I set `start` to the 23rd day of the previous month and `end` to the 13th day of the following month, as this covers all potential days that FullCalendar might possibly display on the initial month view. “start__gte” and “end__lt” are parameters specific to my API. In my case I’m using Django-Tastypie, which leverages to power of Django’s ORM for making queries and filtering data.

    When that transaction is over, the Backbone passes the Collection to FullCalendar to render the initial data. I then call fetch() with no parameters, so it grabs all the data (you might want to tailor this if you have a ton of events). This replaces the existing initial data in the Collection with the complete data set.

    One change I made to Ben’s code is in the “addAll” function, I insert $(this.el).fullCalendar(‘removeEvents’); before the ‘addEventSource’ part, so that when I load the full data set in from the second fetch(), the events from the initial fetch() are cleared and will not be duplicated.

    This resulted in a very noticeable boost in initial page loading, and the user doesn’t even realize data continues to load in the background. Hope that helps!

    • juan
      Posted at 22:35h, 18 April Reply

      Thank you very much!! I tell you my solution, which will improve based on your proposal, two events associated with the prev and next buttons respectively, and thus go to the server to bring events to the days when the calendar is being shown:
      events: {
      ‘click. fc-button-next’, ‘fetch_events’
      ‘click. fc-button-prev’, ‘fetch_events’
      }
      fetch_events: function () {
      var start = this. $ el.fullCalendar (‘getView’). visStart;
      var end = this. $ el.fullCalendar (‘getView’). visEnd;
      this. $ el.fullCalendar (“removeEvents”);
      this.collection.reset ();
      this.collection.fetch ({data: {start: start, end: end}});
      this.addAll ();
      }

      now see how to improve this with your proposal, of course thank you very much!

  • juan
    Posted at 23:49h, 24 April Reply

    Hi, I’m having problems when saving a new event, giving me error:
    fcEvent is undefined…. fcEvent.title = event.get(‘title’);
    EventsView around:
    change: function (event) {
    // Lookup the model that has the ID of the event and update its attributes
    this.el.fullCalendar fcEvent = var (‘clientEvents’ event.get (‘id’)) [0];
    event.get fcEvent.title = (‘title’);
    event.get fcEvent.color = (‘color’);
    this.el.fullCalendar (‘UpdateEvent’, fcEvent);
    }
    on change method to fix this, add the following option “wait: true” in the create eventView.save then is:
    save: function () {
    this.model.set ({
    ‘title’: this. $ (‘# title’). val (),
    ‘color’: this. $ (‘# color’). val ()
    });
    if (this.model.isNew ()) {
    this.collection.create (this.model, {
    success: this.close,
    wait: true / / this lets wait for the server to save the event and return!
    });
    Else {}
    this.model.save ({}, {
    success: this.close
    });
    }

    This event is found and updated, after being saved on the server.
    Hope this helps you, Greetings!

    • John
      Posted at 20:21h, 15 November Reply

      Hi Juan,
      this doesn’t works for me. can you please help

  • victor
    Posted at 19:25h, 29 October Reply

    hi, I have post a question to over stack overflow,
    can you help me?
    http://stackoverflow.com/questions/13115828/fullcalander-in-android-simulator

  • Mohammed Abdulla
    Posted at 04:09h, 10 November Reply

    Hi there,

    I’m getting an error that when I add a new event it updates the text of an existing event with the newly created events title. Furthermore when I click on the newly created event I get the error message this.model is undefined.

    Any ideas anyone?

    • John
      Posted at 20:14h, 15 November Reply

      I am having the same issue. did you get the solution?

  • John
    Posted at 20:13h, 15 November Reply

    When a freshly created event is clicked I am getting the error this.model is undefined. but after a refresh it works fine. the problem is the id doesn’t gets added to the event. can anyone please help me with that?

    • seventhsense
      Posted at 23:38h, 26 January Reply

      I got a same problem.
      I don’t solve this yet.
      But I got a small hint.

      try this..
      addOne: function(event) {
      console.log event
      console.log event.toJSON()
      this.el.fullCalendar(‘renderEvent’, event.toJSON());
      }

      The event Object have a correct id.
      But after toJSON() method, event.toJSON()’s id turned to be undefined.

      I have no idea to solve this yet.

  • Adrian
    Posted at 10:30h, 24 November Reply

    Hey Ben. That was an awesome tut. I know I’m a year late but it was tres useful just the same. I began Backbone a month ago and have a few apps under my belt but for some reason, I still feel overwhelmed with the possibilities, especially when I come across some ingenious use of it that I didn’t think of myself 🙂
    Anyway, my problem today is FullCalendar itself. I need the CSS for selected days altered. I successfully added className ‘fc-selected’ to any selected day which took care of my background colour changes for that selected cell. Thinking that I was home free and only needed to change ‘color’ for the text, i was surprised that it didn’t work and only way later did I realize that the date events are not in the date cell but absolutely positioned above and outside of it.
    Here is where I need your help. How can I target the DOM of those events on selecting a date in the calendar?

    • Ben Teese
      Posted at 07:44h, 07 December Reply

      Hi Adrian,

      I haven’t done much work customizing the CSS for FullCalendar, but my colleague Greg Gross has. Greg suggests:

      “I’ve had a similar problem in the past. The way I handled it was to override the eventRender function (http://arshaw.com/fullcalendar/docs/event_rendering/eventRender/) when initialising my full calendar instance. If you return false from that method, it won’t do it’s default event rendering. You can find the table cell with jQuery and render whatever you want in there.

      eventRender: function(event, element, view) {
      var cell = view.dateCell(event.start);
      var $row = $(‘.fc-content table > tbody > tr:eq(‘ + cell.row + ‘)’);
      var $cell = $row.find(‘td:eq(‘ + cell.col + ‘)’);
      var $content = $cell.find(‘.fc-day-content > div’);
      // render whatever you want in $content
      return false;
      }

      This method worked for me, but it may have other consequences down the road, so you’ll have to try it out and see if it works for you.”

      Hope this helps.

      Cheers,

      Ben

  • victor
    Posted at 12:22h, 27 November Reply

    hi, I have test at chrome’s extension, window resizer with 320 X 480, its seem not fits perfectly, some is short some is taller if got event. how to make it fits perfectly on every mobile device?

    I have post this question to over stack overflow, can you help me? thanks
    http://stackoverflow.com/questions/13511957/fullcalendar-does-not-fit-perfectly-on-mobile

  • x4f4r
    Posted at 18:09h, 09 April Reply

    Hi I am following this tutorial. I am having problem with showing events on calendar. I have following code

    var EventsView = Backbone.View.extend({
    initialize: function () {
    _.bindAll(this);
    this.collection.bind(‘reset’, this.addAll);
    },

    render: function () {
    this.$el.fullCalendar({
    header: {
    left: ‘prev, next today’,
    center: ‘title’,
    right: ‘agendaWeek,agendaDay’,
    ignoreTimezone: false
    },
    defaultView: ‘agendaWeek’,
    selectable: true,
    selectHelper: true,
    editable: true
    });

    return this;
    },

    addAll: function () {
    this.el.fullCalendar(‘addEventSource’, this.collection.toJSON());
    }
    });

    I have a complete question below
    http://stackoverflow.com/questions/15893859/events-are-not-shown-on-calendar-using-jquery-plugin-fullcalendar

  • Pingback:Events are not shown on calendar using jquery plugin fullCalendar | BlogoSfera
    Posted at 16:02h, 11 April Reply

    […] am implementing jquery fullCalendar plugin using backbone following this tutorial. It does show calendar fine, however, it does not show events on calendar at all which I am […]

  • Sanny
    Posted at 12:48h, 14 August Reply

    Hi , I am following this tutorial , i found some issues … My problem is “Uncaught TypeError: Cannot call method ‘create’ of undefined “..

    Here is my code

    $(function(){
    var Event = Backbone.Model.extend();

    var Events = Backbone.Collection.extend({
    model: Event,
    url: ‘events’
    });

    var EventsView = Backbone.View.extend({
    initialize: function(){
    _.bindAll(this);
    this.collection.bind(‘reset’, this.addAll);
    this.collection.bind(‘add’, this.addOne);
    },
    render : function(){
    this.el.fullCalendar({
    header:{
    left: ‘prev,next today’,
    center: ‘title’,
    right: ‘month,basicWeek,basicDay’
    },
    selectable: true,
    selectHelper: true,
    editable: true,
    ignoreTimezone: false,
    select: function(startDate,endDate){
    var eventView = new EventView();
    eventView.collection = this.collection;
    eventView.model = new Event({start : startDate , end : endDate});
    eventView.render();
    }
    });
    },
    select : function(){
    new EventView().render();
    },
    addAll : function(){
    this.el.fullCalendar(‘addEventSource’,this.collection.toJSON());
    },
    addOne : function(event){
    this.el.fullCalendar(‘renderEvent’,event.toJSON());
    },
    create : function(){
    alert();
    }

    });

    var EventView = Backbone.View.extend({
    el : $(‘#eventDialog’),
    initialize : function(){
    _.bindAll(this);
    },
    render : function(){
    this.el.dialog({
    model : true,
    title : ‘New Event’,
    buttons: { ‘Ok’ : this.save , ‘Cancel’: this.close }
    });
    return this;
    },
    save : function(){
    this.model.set({‘title’: this.$(‘#title’).val(), ‘color’: this.$(‘#color’).val()});
    this.collection.create(this.model, {success: this.close});

    },
    close : function(){
    this.el.dialog(‘close’);
    }

    });

    var events = new Events();
    new EventsView({el : $(‘#calendar’) , collection : events}).render();
    events.fetch();
    });

    sorry for my bad english

  • Sanny
    Posted at 18:42h, 29 August Reply

    I’ve got some problem . Please check this out .thanks
    http://stackoverflow.com/questions/18502903/update-my-backbone-collection-whenever-changes-made

  • Dimitris Papageorgiou
    Posted at 02:13h, 09 January Reply

    The problem I am facing has to do with the page that events are supposed to be stored,namely events.The tutorial does not show any code that should go there.As a result in the console of Chrome Dev Tools is the usual 404(not found) message for either when events are fetched (GET) or created (POST).In the beginning the tutorial mentions something about restful API and JSON but no code is shown.Can I get a help, on that. I am in the process of creating a new event.

  • darpan jain
    Posted at 19:12h, 03 February Reply

    Hi Ben,

    Thanks for such a post. It has been very useful for implementation.
    But i am not getting the events when i click “Previous” and “Next” buttons.

    Kindly help

  • Dimitris Papageorgiou
    Posted at 21:37h, 12 February Reply

    The problem I am facing and for which I struggle to find a solution is that for example when I select 15:00 as a start date in the calendar what is acually sent to the server(via ajax) is 13:00.I can see that from the network tab of the chrome dev tools. The same problem applies for the endDate

    Here is an image snapshot(http://sdrv.ms/1buDDJF) from dev tools(network tab).Furthermore, from I search I made there is a function named reportSelection in fullcalendar.js in which the selected dates are passed.I did not see any difference between it(meaning the corresponding variables passed) and the dates selected.

    I do not know what else to do.It does not make sense.

    • Ben Teese
      Posted at 17:13h, 18 February Reply

      What timezone are you in? I’m wondering whether FullCalendar is sending the dates to the server in UTC, which could be different from your local time.

      • Dimitris Papageorgiou
        Posted at 01:30h, 19 February

        I want to say 2 things.
        Why it is important to what timezone I am since what is sent to the server is just what timeslot is selected.If I choose 17:00 for example, that ought to be sent to the server.

        Anyway,my timezone is UTC/GMT +2 hours and probably this is the source of the problem cause as I mention in my post what is sent to the server is lagging behind 2 hours from what I acually select.

        Ok then.HOW I FIX IT?

      • Ben Teese
        Posted at 11:01h, 19 February

        I would recommend that you stick with the approach that FullCalendar is already giving you – ie, converting to UTC as soon as possible and sending that to the server.

        This is the approach I take with all of my apps. In my experience if you don’t do this, it can get confusing when different users in different timezones all start sending different times to your server.

      • Dimitris Papageorgiou
        Posted at 18:58h, 19 February

        The way I see it is that PHP code should made corrections.More specifically adding 2 hours to the time that is sent to the server.Cause if this correction is not made the the timeslots kept in the db WILL NOT BE in accordance with what the user selected in the first place.

  • Dimitris Papageorgiou
    Posted at 20:53h, 12 March Reply

    I am in the process where I have to code the fetching of the events from the database.The above tutorial though does not make any reference though to server-side code(I use PHP not rails).I can see that the backbone fetch method is used to bring the events from the database,but from there I do not know where to start.I would appreciate if someone could give me a hint.

    • Dimitris Papageorgiou
      Posted at 01:58h, 14 March Reply

      something I do not understand why when the backbone fetch method is called no start/end date get not passed in the URL(as get parameters) as the ajax call is made,such as the case when an events option is set(with the an accompanying URL of course)

  • Benjamin Fenton
    Posted at 16:58h, 15 March Reply

    Hello, First of all, thanks so much for the awesome tutorial. Though, I’ve arrived 3 years late, I’m still learning a lot. Currently I’m trying to implement a version w/ Django, Tastypie & Require. Upon trying to click or drag a newly added event, I get the: Cannot read property ‘isNew’ of undefined error. If I refresh the browser, everything works as should. If I throw an extra fetch in there, the event doubles up in the dom and the second rendering of the instance is click-draggable. This question is similar to Sanny’s question (above), but I could not find any responses on any of the forums he posted it to. My code is pretty much identical to yours: https://github.com/benfenton/cali/blob/master/static/js/modules/eventview.js
    Thanks,
    Ben

    • Ben Teese
      Posted at 08:27h, 18 March Reply

      Hi,

      I did a search of your project for ‘FullCalendar’ and noticed a reference in a bower.json file referring to version 1.6.4 of FullCalendar, whereas my project is using version 1.5.2. I’d suggest that as a starting point you investigate whether something has changed in FullCalendar that would cause the problem you are seeing.

      Cheers,

      Ben

      • Benjamin
        Posted at 08:58h, 18 March

        Hi Ben,

        Thanks for getting back to me. I actually solved the issue this morning and was planning to post:

        1. I had not instantiated a new event view in the events view initialize function.

        2. In the event view save function, I needed to set “wait:true” as the first argument of this.collection.create’s success callback.

        Looking forward to reading more from your blog.

        Best,
        Ben

  • Dimitris Papageorgiou
    Posted at 23:33h, 24 March Reply

    According to the code found in application.js-and specifically this line here: this.collection.create(this.model, {success: this.close}); the dialog bog must close upon a successful request to the server.According to backbone manual that happens when a sync event is emitted from the server.In my case nothing is emitted,despite the request being made without errors and the model is saved in the database.What can be wrong?I am making the calendar using PHP.

  • Dimitris Papageorgiou
    Posted at 02:35h, 27 March Reply

    In this tutorial.When the user makes a selection, either in month view, or week view,or day view…the same dialog box opens.Is there a way I can make a distinction and have a different dialog box open depending the view?That means(probably) creating different backbone views but I do not know how to “tie” these with the different calendar views.
    P.S Unfortunately I am banned in stack overflow and I cannot ask questions there.

  • al3via
    Posted at 02:35h, 28 May Reply

    Recurrent events.
    I´m implementing fullCalendar in Backbone, Node JS and Mongo DB. The calendar works fine in Backbone But is sometime a little painful, when things do not want to match the thought. Now I´m working with recurring events.

    The solution to let the Backbone fetch all the events at the beginning works well when only single events exists (“once” events). All events can be loaded in one time and let the backbone manage the collection and pass to FC with the ‘addEventSource’ in the addAll function. But what if we have infinitely recurring event as birthdays?

    Because the nature of the recurrent events, I guess that them must be created “on the fly” on server side. A set of properties (tables) serves as parameter for the creation. The events should then be send to the client as unique event type, with a start and end date property, in order to be parsed in FC.

    To achieve that purpose the client must send the interval of the current view (start and end) to permit the server generate just the events for that period of time.

    GET /events?start=1398549600&end=1402178400&_=1401205606400

    Backbone must catch the buttons clicks for “next , “prev” and all the view. If we use the fullcalendar event function then we are outside Backbone and it seems to be a spaghetti code. No sense to use backbone.

    It would be great to read other opinions about which is the best approach to recurrent events when using FC in backbone.

  • Fernando
    Posted at 05:46h, 31 May Reply

    It’s possible to set background color of a range of dates?
    I’ve tried but I can´t make it work.

  • shashikanth
    Posted at 15:00h, 22 August Reply

    Hi,
    I am getting error as collection undefined at this line of code (this.eventView.collection = this.collection;)

  • Pingback:Fix Eventview.js Errors - Windows XP, Vista, 7 & 8
    Posted at 10:11h, 21 September Reply

    […] Building a shared calendar with Backbone.js and FullCalendar … – FullCalendar lets us detect when a period of time has been selected on the calendar. … My code is pretty much identical to yours: https://github.com/benfenton/cali/blob/master/static/js/modules/eventview.js Thanks, Ben. Reply. Ben Teese says: […]

  • Dimitris Papageorgiou
    Posted at 00:04h, 18 November Reply

    The calendar uses an old backbone version….are there any thought of updating to the newest one.I want to use a backbone plugin(validator) that requires the newest version

  • ActiveEngine Sensei
    Posted at 02:13h, 26 November Reply

    Great post – you really bring the reader from basics to a very advanced level without hurting the brain too much 🙂

    One thing to add that may be of use for performance with a large collection would be fullCalendar(“addEventSource”, calEvents); This function uses a different rendering process that is geared to hide speed updates. I ran into an issue when I had a shared calendar of 100+ items. It was quite surprising that dropping all items and rendering with “addEventSource” was blazingly fast compared to looping through an array then calling the “renderEvent” individually.

    Many tutorials have used qTip as the popup for additional diplay / editing of data. Have you tried this route and have any insights on using qTip vs jQuery UI?

  • Bhavesh Solanki
    Posted at 04:07h, 25 April Reply

    I also want to create calendar in backbone.js but don’t want to use Full Calendar plugin, i want to do hard coded..is there any other way to do this ?

Leave a Reply

Discover more from Shine Solutions Group

Subscribe now to keep reading and get access to the full archive.

Continue reading