Thursday, February 2, 2012

Overriding Operators and Sub-classing in Dart

‹prev | My Chain | next›

Up tonight, I do some quick refactoring of my the View code in my Dart MVC code. I extract out the model code, which looks something like:
#library('Model class describing a comic book');

#import('dart:html');
#import('dart:htmlimpl');
#import('dart:json');

class ComicBook {
  var get attributes;
  var get on;

  ComicBook(this.attributes) { /* ... */ }

  operator [](attr) {
    return attributes[attr];
  }

  save([callback]) { /* ... */ }

  delete([callback]) { /* ... */ }
}

class ModelEvents implements Events { /* ... */ }

class ModelEventList implements EventListenerList { /* ... */ }
I have my attributes and on getters, which provide a mechanism to get the models attributes and subscribe to its events. The save() and delete() methods are simple XHR calls, pulled directly from the old view code. Aside from dispatching events on-success, everything remains the same.

What is new in this class is the operator [] definition to retrieve the value for a given attribute. If the ComicBook is initialized with:
{"title":"Watchmen",
 "author":"Alan Moore",
 "id":"a1f9bcaa4e9939019ec9e4c36fa7a97e"}
Then comic_book['title'] would return "Watchmen". I had originally intended to use the method name get(), just as in Backbone.js. That will not work in Dart since get is a keyword for defining getters. This turns out to be a serendipitous switch because, once I replace the list of hashes with a list of models in the Collection, I need not make any changes to the corresponding view template which had previously expected a Map (a.k.a. Hash):

  _singleComicBookTemplate(comic) {
    return """
      <li id="${comic['id']}">
        ${comic['title']}
        (${comic['author']})
        <a href="#" class="delete">[delete]</a>
      </li>
    """;
  }
That is nice.

Even nicer is that I can extract just about everything out of my Model into a base HipsterModel class:
#library('Base class for Models');

#import('dart:html');
#import('dart:htmlimpl');
#import('dart:json');

class HipsterModel {
  var get attributes;
  var get on;

  HipsterModel(this.attributes) {
    on = new ModelEvents();
  }

  operator [](attr) {
    return attributes[attr];
  }

  get urlRoot() { return ""; }

  save([callback]) { /* ... */ }

  delete([callback]) {
    var req = new XMLHttpRequest();

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

    req.open('delete', "${urlRoot}/${attributes['id']}", true);
    req.send();
  }

}
Then, my actual model class become nothing more than:
#library('Model class describing a comic book');

#import('HipsterModel.dart');

class ComicBook extends HipsterModel {
  ComicBook(attributes) : super(attributes);
  get urlRoot() { return "/comics"; }
}
By overriding the urlRoot() getter, I give the base class al of the information that it needs to do model-like things. That is nowhere near the full power of Backbone.js. But it's arriving in the ballpark's parking lot.

Day #284

No comments:

Post a Comment