05 Aug 2011 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 thetitle
andcolor
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
akismet-a59efb2d5e8c0b994fb39e2d67a207be
Posted at 20:52h, 10 AugustThanks, 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 SeptemberFound 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 NovemberVery 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@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 NovemberI 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 NovemberPopup window doesn’t work(chrome, ff)
Manuel Alzola
Posted at 21:00h, 21 NovemberHello, 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 MayHI 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 NovemberI’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 NovemberHi 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 Decemberon 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 MarchJinu, 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 JanuaryI am also having this issue. Any solutions?
Dimitris Papageorgiou
Posted at 01:49h, 21 MarchHi,did you find a solution to your problem?.I am facing the similar problem
Sachin Gulaya
Posted at 07:36h, 31 DecemberHi 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 JanuaryHi Ben, any chance I can get some advice on how to fix this?
Sachin Gulaya
Posted at 05:20h, 07 JanuaryFinally 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 MarchJust 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 AprilHi 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 AprilI 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 AprilTks 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 AprilHello, 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 Apriljuan — 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 AprilThank 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 AprilHi, 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 NovemberHi Juan,
this doesn’t works for me. can you please help
victor
Posted at 19:25h, 29 Octoberhi, 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 NovemberHi 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 NovemberI am having the same issue. did you get the solution?
John
Posted at 20:13h, 15 NovemberWhen 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 JanuaryI 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.
seventhsense
Posted at 09:30h, 27 JanuaryAt last I got a solution about this problem.
Just add option {wait: true} when create.
All work well now.
http://stackoverflow.com/questions/11628076/backbone-js-getting-id-from-collection-create
Adrian
Posted at 10:30h, 24 NovemberHey 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 DecemberHi 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 Novemberhi, 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 AprilHi 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[…] 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 AugustHi , 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 AugustI’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 JanuaryThe 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.
TaliSeen JahRing
Posted at 14:31h, 09 Januaryhttp://www.sitepoint.com/writing-a-restful-web-service-with-slim/
darpan jain
Posted at 19:12h, 03 FebruaryHi 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 FebruaryThe 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 FebruaryWhat 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 FebruaryI 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 FebruaryI 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 FebruaryThe 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 MarchI 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 Marchsomething 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 MarchHello, 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 MarchHi,
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 MarchHi 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 MarchAccording 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 MarchIn 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 MayRecurrent 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 MayIt’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 AugustHi,
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[…] 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 NovemberThe 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 NovemberGreat 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 AprilI 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 ?