Last night I figured out how to inject shared behavior in Dart. Today I need to use that knowledge to support an injectable
sync
in my HipsterMVC framework. First up, I convert yesterday's proof of concept to something resembling what I envision as the final product. The
HipsterSync
class exposes a setter (sync=
) allowing application code to inject behavior, and a method for internal libraries to invoke the appropriate behavior. I start with:#library('Sync layer for HipsterMVC'); class HipsterSync { // private class variable to hold an application injected sync behavior static var _injected_sync; // setter for the injected sync behavior static set sync(fn) { _injected_sync = fn; } // static method for HipsterModel and HipsterCollection to invoke -- will // forward the call to the appropriate behavior (injected or default) static call(method, model, [options]) { if (_injected_sync == null) { return _default_sync(method, model, options:options); } else { return _injected_sync(method, model, options:options); } } // default sync behavior static _default_sync(method, model, [options]) { print("[_default_sync]"); } }In my
HipsterCollection
base class, I then remove the local storage that I had been doing, replacing it with HipsterSync.call()
:class HipsterCollection implements CollectionWhen I load my app, I see the{ // ... fetch() { HipsterSync.call('get', this); } // ... }
print()
tracer bullets from the current _default_sync()
implementation:Then, in my
main.dart
entry point, I inject a different sync behavior:main() { HipsterSync.sync = (method, model, [options]) { print("[new_sync] $method"); }; // ... }When I load my app now, I see the injected behavior:
Nice. My proof of concept is closer to being reality. Next, I need to do something real with the default implementation. I remove the injected behavior. Then, back in
HipsterSync
, I add the Ajax that used to be in HipsterCollection#fetch()
:#import('dart:html'); class HipsterSync { // ... // default sync behavior static _default_sync(method, model, [options]) { print("[_default_sync]"); var req = new XMLHttpRequest(); req.on.load.add(_handleOnLoad); req.open('get', model.url, true); req.send(); } }Ah. That
_handleOnLoad
callback method is defined back in the collection class. I need a mechanism to pass that into HipsterSync
. Those options
look like a good place to start. In HipsterCollection
, pass the on-load callback via an 'onLoad' key: fetch() {
HipsterSync.call('get', this, options: {
'onLoad': _handleOnLoad
});
}
Then in HipsterSync
, I add the value (_handleOnLoad
) to the list of on.load
event handlers for the Ajax request: // default sync behavior
static _default_sync(method, model, [options]) {
print("[_default_sync]");
if (options == null) options = {};
var req = new XMLHttpRequest();
if (options.containsKey('onLoad')) {
req.on.load.add(options['onLoad']);
}
req.open('get', model.url, true);
req.send();
}
Trying this out, it works and I have my application again loading my comic book collection over Ajax:Now for the moment of truth—I define a local storage sync behavior in
main.dart
:main() { HipsterSync.sync = (method, model, [options]) { print("[local_sync] $method"); if (method == 'get') { var json = window.localStorage.getItem(model.url), data = (json == null) ? {} : JSON.parse(json); if (options is Map && options.containsKey('onLoad')) { options['onLoad'](data.getValues()); } } }; // ... }In here, I have adapted my local storage solution. In order for both local storage and Ajax to submit data to the on-load callback, I had to alter the callback to expect data rather than an event from which the data could be extracted. Once that is done, the injected behavior of local storage is working:
Cool! I can switch back and forth between local storage and the default Ajax implementation by adding and removing the
HipsterSync.sync
local behavior. I still need to get all of this working with my model class, but hopefully that will prove relatively easy now that I have my collection working properly.Day #297
No comments:
Post a Comment