Thursday, September 1, 2011

jQuery UI and Backbone.js

‹prev | My Chain | next›

Before doing anything else with my little Backbone.js calendar application, I would like to be able to add new appointments / calendar events. I have been doing that via the CouchDB backend and it is getting a bit old. Besides, with the new month, most of my events have disappeared:
But how to add these appointments?

I rather fancy a jQuery-ui modal dialog box that pops up when I click on the appropriate day. But, I have no idea where to hook the jQuery-ui dialog into my Backbone app...

First things first, I download and install jQuery-ui (the javascript and the theme css) and add it to my Jade layout template:
!!!
html
  head
    title= title
    link(rel='stylesheet', href='/stylesheets/style.css')
    link(rel='stylesheet', href='/stylesheets/blitzer/jquery-ui.css')
    script(src='/javascripts/jquery.min.js')
    script(src='/javascripts/jquery-ui.min.js')
    script(src='/javascripts/underscore.js')
    script(src='/javascripts/backbone.js')
  body!= body
Next, I create a very simple dialog in the Jade template:
#dialog(title="Add calendar event")
  #calendar-event-start-date
  p title
  p
    input#calendar-event-title(type="text", name="title")
  p description
  p
    input#calendar-event-description(type="text", name="description")
I have the intention of eventually grabbing the values for appointments from the two dialog fields and from the #calendar-event-start-date <div> (which I will populate from the date clicked). But before I reach that point, I need to make this a jQuery-ui dialog:
script
  $(function() {
    $('#dialog').dialog({
      autoOpen: false,
      modal: true,
      buttons: [
        { text: "OK",
          click: function() { $(this).dialog("close"); } },
        { text: "Cancel",
          click: function() { $(this).dialog("close"); } }
      ]
    });
  });
So far, there is absolutely nothing Backbone-y about this. I change that by adding an AppView Backbone View class:

    window.AppView = Backbone.View.extend({
      el: $("#dialog"),
      events: {
        'click .ok':  'create'
      },
      create: function() {
        console.log("here");
        Events.create({
          title: "foo",
          description: "bar",
          startDate: "2011-09-01"});
      }
    });

    window.AppView = new AppView;
After reloading the page, I open that dialog from the Javascript console:
$('#dialog').dialog('open')
And I am greeted with a right proper jQuery-ui dialog:
Unfortunately, when I click the "OK" button, nothing happens. Well, the dialog closes (the behavior specified in my jQuery-ui dialog() invocation. But a new Event is not create. Even the console.log() statement is not reached.

Hrm...

Eventually, I track this down to two things. First, I need to set the el attribute to the dialog's parent:
    window.AppView = Backbone.View.extend({
      el: $("#dialog").parent(),
      // ...
   });
This way, the wrapper divs added by jQuery-ui become the element for this view. Also, I need to add a class to the OK button:

script
  $(function() {
    $('#dialog').dialog({
      autoOpen: false,
      modal: true,
      buttons: [
        { text: "OK",
          class: "ok",
          click: function() { $(this).dialog("close"); } },
        { text: "Cancel",
          click: function() { $(this).dialog("close"); } }
      ]
    });
  });
With that, I reach my console.log statement and I try to create my event:


Well, once I create the backend POST route, I ought to able to create appointments.

The POST route in my express.js needs to POST the submitted JSON to CouchDB as 'application/json' data. Thus, my POST route is:
app.post('/events', function(req, res){
  var options = {
    method: 'POST',
    host: 'localhost',
    port: 5984,
    path: '/calendar',
    headers: {'content-type': 'application/json'}
  };

  var couch_req = http.request(options, function(couch_response) {
    console.log("Got response: %s %s:%d%s", couch_response.statusCode, options.host, options.port, options.path);

    couch_response.pipe(res);
  }).on('error', function(e) {
    console.log("Got error: " + e.message);
  });

  couch_req.write(JSON.stringify(req.body));
  couch_req.end();
});
Aside from the headers and the write() of the JSON data to the CouchDB request, the remainder of this route looks very similar to stuff that I have been writing for GETs and DELETEs over the past few days. It may be time to investigate adding an abstraction layer in my express app. Another day, perhaps.

With the backend POST route, I am able to create appointments. I still have a bunch of cleanup to do in this, but I think I am off to a good start. I will pick back up here tomorrow.

Day #130

No comments:

Post a Comment