Skip to content
Declarative event handling and binding
JavaScript CoffeeScript CSS Other
Branch: master
Clone or download
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Permalink
Type Name Latest commit message Commit time
Failed to load latest commit information.
coffee-cj
coffee
demo
lib-cj
lib
.gitignore
README.md
bower.json
build
package.json
test-inheritance.coffee
yarn.lock

README.md

SuperEmitter

Event handling and binding

If you are writing your own GUI components, or you are just tired of event-binding boilerplate, this package is what you need. Out of the box:

  • Declarative event binding, with concise format, very clear about event source, name and its handlers
  • Event handling logic is now a data structure (!!), not code
  • Event handling inheritance
  • Event handling composition
  • Light-weight event emission (no built-in bubble/capture), but with arguments

Code weirds

# I use different parentheses styles to separate method calls from function calls: 
obj.method()
(function1 (function2 arg1), arg2)
# I don't use brackets on methods, if I pass anonymous multiline functions.
$jquery_element.on 'click', (e) ->
  console.log('wow')

How it can change your life:

# Your old boilerplate to bind handlers to events:
class EventSpaghetti
  constructor: ->
    $some_jquery.on 'click', (e) ->
      task1(e)
      task2(e)
      task3(e)

    @non_jquery_emitter.on 'server_event', (e) ->
      if (event_is_valid e)
        @update_view(e)

new EventSpaghetti();

# Your new boilerplate
class Component extends SuperEmitter
  event_table: [ 
    [ $some_jquery        , [ [ 'click'       , [ task1,
                                                  task2,
                                                  task3          ] ] ] ]
    [ 'non_jquery_emitter', [ [ 'server_event', [ event_is_valid,
                                                  'update_view'  ] ] ] ]
  ]
new Component().bind_events()

Demo

Clone the repo, open demo/index.html in browser, move your mouse, click on blocks, look at console output.

Read the demo/app.coffee it's very short.

Ask me.

Docs

Binding events

Create emitter class, describe its emitters, events and reactions in event_table format:

# [ [ emitter_name, [ [ event_name, [ reactions... ] ] ] ] ]
# let me show it trough simple composition
# I will write a class descibing a brain of an ancient human,
# That should be able to handle various events.

class Brain extends SuperEmitter
  # Events
  event_table: [
    [ 'ear' , [ [ 'snake_heard'     , [ 'emit_adrenaline'
                                        'look_around'        ] ] ] ]
    [ 'eye' , [ [ 'food_spotted'    , [ 'emit_noradrenaline'
                                        'hunt'
                                        'emit_endorphins'    ] ]
                [ 'predator_spotted', [ 'emit_cortisol'
                                        'emit_adrenaline'
                                        'run'                ] ] ] ]
    [ 'nose', [ [ 'food_smelled'    , [ 'look_around'        ] ]
                [ 'blood_smelled'   , [ 'emit_adrenaline'
                                        'look_around'        ] ] ] ]
  ]

  # constructor and instance members
  constructor = ->
    @ear  = new Ear()
    @eye  = new Eye()
    @nose = new Nose()


  # methods:
  emit_adrenaline: ->
  emit_cortisol: ->
  emit_endorphins: ->
  hunt: ->
  look_around: ->

# The event table can be decomposed as following
flee_reactions = [ 'emit_cortisol', 'emit_adrenaline', 'run' ]
hunt_reactions = [ 'emit_noradrenaline', 'hunt', 'emit_endorphins' ]
seek_reactions = [ 'look_around' ]
watch_outs     = [ 'emit_adrenaline', 'look_around' ]

ear_events  = [ [ 'snake_heard'     , watch_outs     ] ]
eye_events  = [ [ 'food_spotted'    , hunt_reactions ]
                [ 'predator_spotted', flee_reactions ] ]
nose_events = [ [ 'food_smelled'    , seek_reactions
                  'blood_smelled'   , watch_outs     ] ]

ear_pack  = [ 'ear' , ear_events  ]
eye_pack  = [ 'eye' , eye_events  ]
nose_pack = [ 'nose', nose_events ]

Brain::event_table = [ ear_pack, eye_pack, nose_pack ]

Emitting events

# Use in a method
class Brain extends SuperEmitter
  # ... event table and stuff ...
  # simplest form
  emit_adrenaline: ->
    @emit('adrenaline')

  # or with arguments
  emit_adrenaline: (dose_ml = 0.02, delay_ms = 500, noradrenaline = false) ->
    @emit('adrenaline', [dose_ml, delay_ms, noradrenaline])
    # square brackets used to denote arguments from event name

Receiving events

class Brain extends SuperEmitter
  # the ones called without args
  receive_adrenaline: ->
    console.log arguments.length # -> 0

  # the ones called with args
  receive_adrenaline: (dose_ml, delay_ms, noradrenaline) ->
    console.log arguments

Event handling is data now

And you can do with it anything you can do with data. Equality, cloning and merging.

Merging event tables

There is a class_tools module, which contains merge_events function. It is a pure function merges two or more event tables into one. New items will be added, repeating ones will be removed.

table1 = [ [ 'nose', [ [ 'smell-cheese', [ 'look_around' ] ] ] ]
           [ 'eye' , [ [ 'sought-food' , [ 'grab_item'   ] ] ] ] ]

table2 = [ [ 'nose', [ [ 'smell-cheese', [ 'search_food' ] ] ] ]
           [ 'eye' , [ [ 'sought-food' , [ 'grab_item'
                                           'chew_item'   ] ] ] ] ]

(class_tools.merge_events table1, table2) # -> will produce:
[ [ 'nose', [ [ 'smell-cheese', [ 'look_around'
                                  'search_food' ] ] ] ]
  [ 'eye' , [ [ 'sought-food' , [ 'grab_item'
                                  'chew_item'   ] ] ] ] ]

License

MIT

You can’t perform that action at this time.