1
votes

I am developing a Dart based wrapper for the Google Visualization / Charts API that can be viewed over at github already. I created a chart specific wrapper classes for all the charts that are available.

Right now I am looking into the events of the underlying classes. The problem is that there are various events that are not shared across all charts. Thus I need to find a way to assign specific event types to their corresponding charts. What I already did is:

  1. I created a wrapper for the Events api:

    abstract class Events {
      static JsObject addListener(source_visualization, event_name, Function handling_function) {
        return vis['events'].callMethod('addListener', [source_visualization, event_name, new JsFunction.withThis(handling_function)]);
      }
    
      static JsObject addOneTimeListener(source_visualization, event_name, handling_function(e)) {
        return vis['events'].callMethod('addOneTimeListener', [source_visualization, event_name, new JsFunction.withThis(handling_function)]);
      }
    
      static void removeListener(listener_handler) {
        return vis['events'].callMethod('removeListener', [listener_handler]);
      }
    
      /// TODO(rh): removeAllListeners
    }
    
  2. I created a wrapper class that encapsulates all the code for one event The code looks like this:

    class EventWrapper<E extends Event> {
      JsObject _jsChart;
      String _eventName;
      JsObject _handler;
      Stream<E> get onEvent => _streamController.stream;
      StreamController<E> _streamController;
      ...
      EventWrapper(this._jsChart, this._eventName, E reviver(p)) {
        _streamController = new StreamController.broadcast(onListen: () => _onListen(_eventName), onCancel: () => _onCancel(_eventName));
      }
    
      void _onListen(String event) {
        _handler = Events.addListener(_jsChart, event, ...);
      }
    
      void _onCancel(String event) {
        Events.removeListener(_handler);
      }
    }
    
  3. In the chart you can use the code like this:

    class PieChart ... {
      Stream<SelectEvent> get onSelect => _selectEventHandler.onEvent;
      EventWrapper _selectEventHandler;
    
      PieChart(Element e) : super._(e, "PieChart", vis) {
        _selectEventHandler = new EventWrapper<SelectEvent>(jsChart, 'select', (p) => new SelectEvent());
        // Other Events
      }
      // ...
    }
    

The problem is the following: The Events interface requires a source_visulization argument, that is the chart I want to register events on. But as I use dart:js this is only available AFTER I called the constructor on my chart, because this creates the object via JsObject (jsChart = new JsObject(ctx[chartName], [element])). Usually I would create the EventWrapper instances within the constructor, but as I have to use the events in several classes I thought of two other solutions than the current on you can see at #3.

  1. Create a mixin - this does not really work, because I do not have a constructor and I cannot specify an instsance method because it gives me an error:

    abstract class SelectEventMixin {
      Stream<SelectEvent> get onSelect => _selectEventHandler.stream;
      final StreamController _selectEventHandler = new StreamController.broadcast(onListen: _onListen);
    
      void _onListen() {
        // jsChart is available here
      }
    }
    
  2. Create BaseClasses for each combination of events seen, this would allow me to share the most common code. Some BaseClasses would be: NoEvents, SelectEvent, SelectReadyEvents, SelectReadyAnimationFinishEvents, ...

Does anyone of you have a better solution? Or can somebody tell me how to fix my problem #1? Is #2 already the best solution? As I would only have to combine the several methods and only have a little duplicated code?