
Declarative event binding / handling
Hello! I want to show my piece of CoffeeScript for declarative subscription and event handling.
I have 5 years of engineering experience, including .NET (+ forms, + WPF, + .NET MVC), Java (+ Swing, + Tapestry5, + Groovy), JavaScript (+ CoffeeScript, + Node).
Last year, I have been actively writing my own single-page web application that works without rebooting (which will be discussed in future releases). The entire dynamic part of the user interface is created on the client, only data comes from the server. As often happens in the UI, I deal with the component tree. Of course, to organize tree interaction, I need a mechanism for sending and processing events. I decided to write my own, and not use Backbone, or something from Google Closure. In any case, I had experience implementing the Listener pattern etude.
The Event Emitter Class of the first version, if you wanted to listen to it, just wrote you into an array of listeners. When the “Ch” event occurred, he went around the array, and looked for listeners with the “onCh” method, and called it. Type as in Swing / .NET.
Everything worked great for a small number of Emitters. With the growth of the system, the problem of overlapping event names between different emitters came. The Subscriber performed the same “HF” method, even if the “H” came from different Emitters. Then it was more familiar to JS, EventEmitter, as in jQuery / NodeJS. I will not particularly delay ...
Now I am working with a tabular representation of the tree "emitter -> events -> reactions". Example:
Instead:
This is my ognivo / super-emitter library . It allows you to remove all the connecting code, and focus on sources, events and reactions. The code is less, it is cleaner, more granular. The format allows you to see what and where the response sequence comes from.
Moreover, a table is data, and with it you can do everything you can do with data. Compare, glue, clone and so on.
Demo online . This is from the demo folder in the repo. You can clone the repo, you can do
SuperEmitter is implemented as a class. It has an emit method through which an event is issued and its arguments are passed. Arguments must be packed into an array when called, which makes them stand out visually. An event-subscribed function accepts arguments through a regular parameter list.
The instance you are subscribing to should have a “on” method with a signature (is there a Russian word?) Event_name, callback. Suitable jQuery wrappers, KineticJS shapes, shapes of the SuperEmitter class.
The addEventListener method, standard for DOM components, has the same signature, so there’s nothing complicated with it either. Once I needed to subscribe to popstate from a window, so everything was decided window.on = window.addEventListener.
There is no ascent and interception of events. I don’t see the reasons for doing so far, I like that for each level of the component hierarchy I see all the events that pass through it.
I will be glad to criticism, praise and additions.
ZY: I intentionally chose the word emitter, not the source.
Prehistory
I have 5 years of engineering experience, including .NET (+ forms, + WPF, + .NET MVC), Java (+ Swing, + Tapestry5, + Groovy), JavaScript (+ CoffeeScript, + Node).
Last year, I have been actively writing my own single-page web application that works without rebooting (which will be discussed in future releases). The entire dynamic part of the user interface is created on the client, only data comes from the server. As often happens in the UI, I deal with the component tree. Of course, to organize tree interaction, I need a mechanism for sending and processing events. I decided to write my own, and not use Backbone, or something from Google Closure. In any case, I had experience implementing the Listener pattern etude.
The Event Emitter Class of the first version, if you wanted to listen to it, just wrote you into an array of listeners. When the “Ch” event occurred, he went around the array, and looked for listeners with the “onCh” method, and called it. Type as in Swing / .NET.
Everything worked great for a small number of Emitters. With the growth of the system, the problem of overlapping event names between different emitters came. The Subscriber performed the same “HF” method, even if the “H” came from different Emitters. Then it was more familiar to JS, EventEmitter, as in jQuery / NodeJS. I will not particularly delay ...
Supermitter
Now I am working with a tabular representation of the tree "emitter -> events -> reactions". Example:
class Brain extends SuperEmitter
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: ->
new Brain().bind_events()
Instead:
class Brain
bind_events: ->
@ear.on 'snake_heard', (args...) => # условные аргументы
@emit_adrenaline(args...) # условная передача аргументов
@look_around(args...)
@eye.on 'food_spotted', (args...) =>
@emit_noradrenaline(args...)
@hunt(args...)
@emit_endorphins(args...)
@eye.on 'predator_spotted', (args...) =>
@emit_cortisol(args...)
@emit_adrenaline(args...)
@run(args...)
@nose.on 'food_smelled', (args...) =>
@look_around(args...)
@nose.on 'blood_smelled', (args...) =>
@emit_adrenaline(args...)
@look_around(args...)
# и т.д.
This is my ognivo / super-emitter library . It allows you to remove all the connecting code, and focus on sources, events and reactions. The code is less, it is cleaner, more granular. The format allows you to see what and where the response sequence comes from.
Moreover, a table is data, and with it you can do everything you can do with data. Compare, glue, clone and so on.
Demo
Demo online . This is from the demo folder in the repo. You can clone the repo, you can do
npm install super-emitter
. I want to make a larger demo, but for now it’s tight with time.Issue and reception of events
SuperEmitter is implemented as a class. It has an emit method through which an event is issued and its arguments are passed. Arguments must be packed into an array when called, which makes them stand out visually. An event-subscribed function accepts arguments through a regular parameter list.
brain = new Brain()
brain.bind_events()
brain.emit('adrenaline')
brain.emit('adrenaline', [dosage_ml = 300, noradrenaline = true])
brain.on 'adrenaline', (dosage_ml, noradrenaline) ->
console.log "I'm running from something"
# OR methods
class Brain extends SuperEmitter
emit_adrenaline: (dosage_ml = 10, noradrenaline = true) ->
@emit('adrenaline, [dosage_ml, noradrenaline])
receive_adrenaline: (dosage, noradrenaline) ->
if noradrenaline
console.log "I will fight to death"
else
console.log "Let's get outta here"
Requirements
The instance you are subscribing to should have a “on” method with a signature (is there a Russian word?) Event_name, callback. Suitable jQuery wrappers, KineticJS shapes, shapes of the SuperEmitter class.
The addEventListener method, standard for DOM components, has the same signature, so there’s nothing complicated with it either. Once I needed to subscribe to popstate from a window, so everything was decided window.on = window.addEventListener.
What not
There is no ascent and interception of events. I don’t see the reasons for doing so far, I like that for each level of the component hierarchy I see all the events that pass through it.
I will be glad to criticism, praise and additions.
ZY: I intentionally chose the word emitter, not the source.