Thursday, February 9, 2012

Long Distance Events in Dart

‹prev | My Chain | next›

I made it through a chunk of refactoring in my Dart based minimal MVC framework yesterday. I am still not quite done, however. My ultimate goal is to reach a point at which adding to a collection will fire an event for which a collection view can listen and react.

As of last night, I have a view that creates a new model on the collection using the create() method in the HipsterCollection base class:
class HipsterCollection {
  // ...
  create(attrs) {
    var new_model = model(attrs);
    new_model.save(callback:(event) {
      this.add(new_model);
    });
  }

  add(model) {
    models.add(model);
    on.add.
      dispatch(new CollectionEvent('add', this, model:model));
  }
  // ...
}
After a new model is saved, it is added to the collection. The add() method also dispatches an add event, for which the collection view can listen:
#library('Collection View for My Comic Book Collection');

#import('HipsterView.dart');

class Comics extends HipsterView {
  Comics([collection, model, el]):
    super(collection:collection, model:model, el:el);

  post_initialize() {
    _subscribeEvents();
    // ... 
  }

  _subscribeEvents() {
    if (collection == null) return;

    collection.on.load.add((event) { render(); });
    collection.on.add.add((event) { render(); });
  }
  // ...
}
The add.add thing is a little weird (add an event listener for the add event). Since I am defining these events myself, I will have to come up with a better name. But, for now, it works. When I add couple of test comic books to my collection, they are immediately added to the UI:


Yay!

That was sort of anti-climatic. I have struggled much getting to this point. I did not expect it to just work. Ah well.

There is a minor problem. I cannot delete the newly added comic books. This turns out to be due to a lack of ID, which I add to the enclosing <li> tag:


If I reload the page, the new items have an ID and I can delete them. So something must be going wrong in the model creation. Looking at the save() method in my HipsterModel, I think I see what. I am not adding the response from the server to the internal attributes:
class HipsterModel {
  // ...
  save([callback]) {
    var req = new XMLHttpRequest()
      , json = JSON.stringify(attributes);

    req.on.load.add((event) {
      print("[save] ${req.responseText}");
      on.save.dispatch(event);
      if (callback != null) callback(event);
    });

    print("[save] $json");
    req.open('post', '/comics', true);
    req.setRequestHeader('Content-type', 'application/json');
    req.send(json);
  }
  // ...
}
I print the response to the console, but I am not doing anything with it. And, indeed, the response does contain the ID from the server:


It is easy enough to parse that JSON response so that the model's attributes can be updated accordingly:
class HipsterModel {
  // ...
  save([callback]) {
    var req = new XMLHttpRequest()
      , json = JSON.stringify(attributes);

    req.on.load.add((event) {
      attributes = JSON.parse(req.responseText);
      on.save.dispatch(event);
      if (callback != null) callback(event);
    });

    req.open('post', '/comics', true);
    req.setRequestHeader('Content-type', 'application/json');
    req.send(json);
  }
  // ...
}
With that, I can add comics to my collection, have them show up on the page, and delete them. And all of this is reflected in the backend datastore:


That is a fine stopping point for tonight. There is still a ton to do before I am even close to replicating something like Backbone.js in Dart (and I have no intention of doing that), but I feel as though I am off to a pretty solid start. I am especially pleased with how easy it was to achieve the same separation of concerns through events that Backbone boasts.

Up tomorrow: I make my minimal framework a little more Dart-like starting with exploring Generics.


Day #291

No comments:

Post a Comment