Skip to content

Loading…

client-side @on after domready #51

Closed
scien opened this Issue · 7 comments

2 participants

@scien
ZappaJS member

the following logs "bar1" and "bar2"

My understanding from this is that you can't add a new event after the dom is ready. This stackoverflow thread had the same problem (http://stackoverflow.com/questions/6483862/socket-io-server-emits-not-firing).

Is there anyway to accomplish this? I want to listen for new events in reaction to a user's actions.

require('./zappajs') ->

  @get '/': ->
    @render index: {layout: no}

  @on foo: ->
    @emit "bar#{n}", {} for n in [1..5]

  @client '/index.js': ->
    @connect()

    _io = @
    @on bar1: ->
      console.log 'bar1'

    class Outer1
      constructor: ->
        _io.on bar2: ->
          console.log 'bar2'
    class Outer2
      constructor: ->
        _io.on bar3: ->
          console.log 'bar3'

    new Outer1()
    $ ->
      new Outer2()

      _io.on bar4: ->
        console.log 'bar4'

      class Inner
        constructor: ->
          _io.on bar5: ->
            console.log 'bar5'
      new Inner()

      _io.emit 'foo', {}

  @view index: ->
    doctype 5
    html ->
      head ->
        script src: '/zappa/Zappa-simple.js'
        script src: '/index.js'
@shimaore
ZappaJS member

The way @on is written you effectively can't use it dynamically -- the handlers need to be rewritten to insert helpers and replace this, and that step is only done once.

However I think your code could work, except that native Socket.IO doesn't support

_io.on bar1: ->

only

_io.on 'bar1', ->
@scien
ZappaJS member
_io.socket.on 'bar1', ->

works, but of course the context is different than the other @on listeners

just to note another disadvantage of using the shortcuts is that you can't have multiple listeners on the same event

@connect()
@on bar1: ->
  console.log 'bar1'
@on bar1: ->
  console.log 'bar1-2'

socket = io.connect '/'
socket.on 'bar2', (data) ->
  console.log 'bar2'
socket.on 'bar2', (data) ->
  console.log 'bar2-2'

(with the same emit 'foo' from above...) logs "bar1-2", "bar2", "bar2-2".

and adding a listener inside a class is also a pain because you'd generally want @ to refer to the class and not the zappa context

I think I'll just use io directly to avoid confusion in the project I'm working on. Is it worth adding any of these features to the zappa shortcuts though? (1. dynamic use of @on, 2. multiple listeners for same event, 3. some useful way to use a class method as the handler for an event)

@shimaore
ZappaJS member

@scien : not ignoring you, just swamped with work. :)

These are all good comments. Only thing I can say so far is that there's a slight performance advantage in not using more parameters than needed -- that was noticeable in the benchmarks when I removed extraneous parameters from the Express part of zappajs (in other words, apply ctx is slightly faster than apply ctx, foo). I guess we can always make the "use parameters model for socket.io" behavior an option.

I'm especially bummed by the "single listerner" issue, honestly.

For the "zappa with classes" issue, can you recommend a solution?
Should we add some way to create a mixin between the class you create and a Zappa instance for example? (Which methods should we inject in that case?)

@scien
ZappaJS member

server-side I ended up using zappa. I just made sure to document expected params and namespace events.

client-side I'm loading socket.io.js directly (no zappa). However, the classes i made ended up being similar to zappa style, and with io.connect I can listen to the same event multiple times. For the class, I set it up so you define "events" and "emitters".

Class Channel extends Socket
  events:
    'onUserJoin': 'user:join'
    'onUserLeave': 'user:leave'
  emitters:
    'join': 'channel:join'
    'leave': 'channel:leave'
  onUserJoin: () ->
    console.log 'user joined'
  onUserLeave: () ->
    console.log 'user left'

This sets up a method to be called on the class when a specific event happens, and creates methods on the class that are basically a shortcut to socket.emit event, data, ack.

I also added "on" and "emit" methods so you can add/send new messages on the fly.

c = new Channel()
c.join 'hungry'
c.on 'food:eat', (data) ->
   console.log "#{data.user} at an #{data.name}"
c.emit 'food:eat', {user: 'scien', name: 'apple'}, () ->
  console.log 'event was sent'

I'm not sure how to best work this into zappa yet. We could just make the Socket (or w/e it'll be called) class available in the client-side root scope. Things to consider would be
1. how to give Socket access to the connection ("socket = io.connect '/'"), right now I just pass the socket into the Channel class (so I only do io.connect once for all instances of Channel)
2. join/leave should probably be methods on Socket (they should be readily available), this currently requires a @on listener on the server, and then @join from there. can we effectively make @join/@leave available in the client-side scope?
3. side note: with emit/on in the case above, join and then emit are made to appear synchronous, but the class waits until you receive the ack from join to process the on and emits.
4. my case assumes the class is bound to one channel. The same syntax would work if you didn't use rooms/channels and the emit would just go to everyone. I can't think of a case where'd you use one class for multiple rooms/channels because they'd likely have different functionality or want to be handled separately. Can you think of a case where the class should allow multiple @joins?

@shimaore
ZappaJS member

FWIW there doesn't seem to be join or leave in the client socket object. This makes sense: the server controls which clients may join/leave a room.

@scien if possible, can you give 10dfb4d a run? I don't understand why things were done the way they were done before: @connect() should always be called first, but then there's no reasons to postpone running @on sequences as long as the helpers are defined earlier -- so I'm probably missing some tricky details wrt socket.io's behavior or something.

I guess it would help to have others test this change as well.

@shimaore
ZappaJS member

The original example at the top of this issue shows bar1 through bar5 with the changes in 10dfb4d
Also the second example shows both bar1 and bar1-2.

@scien
ZappaJS member

original example and updated chat example both work for me with this new change

@shimaore shimaore closed this
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Something went wrong with that request. Please try again.