Monday, January 30, 2012

Custom Events in Dart

‹prev | My Chain | next›

I have a deadline for the alpha release for Dart for Hipsters, but before I can get to that, I must appease the gods of my chain. I have yet to meet these gods, but they have been kind to me, so I must do what I can to keep them happy.

Last night I got started breaking my Ajax based Dart application into real View classes (somewhat along the lines of Backbone.js). Today, I hope to extract collection code from my view into a proper collection class.

The heart of the collection, the fetching from the backend datastore, I can already do:
class ComicsCollection {
  var list;

  ComicsCollection() {
  }

  // Be Backbone like
  fetch() {
    var req = new XMLHttpRequest();

    req.on.load.add(_handleOnLoad);
    req.open('get', '/comics', true);
    req.send();
  }

  _handleOnLoad(event) {
    var request = event.target;

    list = JSON.parse(request.responseText);

    // How to tell the view that I'm done?
  }
}
But how can I communicate to my view that the collection has loaded new data?

I would like to instantiate a collection and a collection view something along the lines of:
  var my_comics_collection = new ComicsCollection()
    , comics_view = new ComicsCollectionView(
        el:'#comics-list',
        collection: my_comics_collection
      );

  my_comics_collection.fetch();
In the collection view, I could then subscribe to the collection view's on-load event:
  ComicsCollectionView([el, collection]) {
    this.el = document.query(el);
    this.collection = collection;

    collection.on.load.add((event) {
      render();
    });
  });
To get that to work, I will at least need an on getter in the Collection:
class ComicsCollection {
  var list;
  var get on;

  ComicsCollection() {
    on = new CollectionEvents();
  }
}
I could probably re-use one of the DOM Events classes here, but it is not too hard to define my own:
class CollectionEvents implements Events {
  var load_list;

  CollectionEvents() {
    load_list = new CollectionEventList();
  }

  get load() {
    return load_list;
  }
}
I defined a load getter that returns an EventList object. This CollectionEventList class that I define will be the thing that holds event listeners. It needs to define two methods: add() to add listeners and dispatch() to send events to all listeners:
class CollectionEventList implements EventListenerList {
  var listeners;

  CollectionEventList() {
    listeners = [];
  }

  add(fn) {
    listeners.add(fn);
  }

  bool dispatch(Event event) {
    listeners.forEach((fn) {fn(event);});
    return true;
  }
}
No doubt I can be a little more elegant about that, but that should meet the spirit of what is needed. Amazingly, that does work. To be sure that I am not somehow faking it, I add another debugging on-load handler to listen for my custom event:
class ComicsCollectionView {
  var get el;
  var get collection;

  ComicsCollectionView([el, collection]) {
    this.el = document.query(el);
    this.collection = collection;

    collection.on.load.add((event) {
      render();
    });

    collection.on.load.add((event) {
      print("This really did fire in response to a custom event");
    });

    _attachUiHandlers();
  }

  // ...
}
And indeed, it does work:


Nice. That almost seems a little too easy. I will take a look at this in the fresh light of a new day to make sure that I am not cheating somehow. If custom event handlers in Dart really are this easy, then I am thrilled. Bust mostly I'm sleepy.


Day #282

2 comments:

  1. I bought your book last week and working through building the ComicsCollection class.

    I knew that the book may be slightly outdated with the language when I bought it, but thought I'd be able to figure out the differences.

    There are two places where I'm a bit stuck right now, which I'm sure in time I'll figure out, but maybe you can point me in the right direction.

    Firstly: ``no such type "CollectionEvents"``
    Secondly: ``responseText is not a member of EventTarget``

    Thanks :)

    ReplyDelete
    Replies
    1. It's crazy. We just put out an update two months ago and it's already out of date! Darn those Dart folks and their amazing productivity :P

      Are you getting these errors when working through the book or this post? If the book, can you let me know the page in PDF and the edition?

      Anyhow, the `no such type "CollectionEvents"` error is most likely because you need to define that class. It is defined above, so if you copy that, it should work.

      The responseText error may be related to the change from XMLHttpRequest to HttpRequest:
      http://news.dartlang.org/2012/08/breaking-change-xmlhttprequest-becomes.html.

      I'm working through the changes for M1 now though: http://japhr.blogspot.com/search/label/dartlang. Hopefully we'll get in sync with M1 shortly.

      Delete