Code Monkey home page Code Monkey logo

Comments (10)

btford avatar btford commented on July 29, 2024

Hah, this was the next thing on my list.

Ideally, it'd be nice for Angular to automatically pass socket to the factory, so the entire setup/teardown is transparent. I don't think there's a nice way to do that right now, so passing $scope as an arg might be totally reasonable.

I'm going to think on it a bit and possibly consult my colleagues.

from angular-socket-io.

btford avatar btford commented on July 29, 2024

Another thought I had was to use module.decorator to augment $scope with a socket API that cleans itself up when the scope is destroyed. I don't particularly like this solution though because it makes it less clear where the $scope.socket comes from.

from angular-socket-io.

BinaryMuse avatar BinaryMuse commented on July 29, 2024

I agree about module.decorator; I think given the choice I'd rather explicitly tie the service to the scope.

Have you given any thought to/how do you feel about a change to $rootScope that would either

  1. allow you to be notified when any child scope of a scope is destroyed (e.g. Scope#$destroy calls $rootScope.$emit or its own $emit with some new event), allowing you to watch for a $destroy event on any of a given set of scopes
  2. similarly, allow you to be notified when new scopes are created (e.g. Scope#$new calls $rootScope.$emit with some new event), allowing you to keep track of any created scopes, thus allowing you to watch for their $destroy events

The topic on the AngularJS mailing list that lead to a discussion of the new scope event system (Event Bus in AngularJS) brought up the same kind of issue; some of the talk there makes me wonder if making it too easy to leak memory is a worry with this kind of technique.

from angular-socket-io.

btford avatar btford commented on July 29, 2024

New idea: forward socket events to $rootScope.broadcast, prefixing the event name with socket: or similar. This approach should gracefully handle scope deletion.

from angular-socket-io.

JogoShugh avatar JogoShugh commented on July 29, 2024

btford: the forwarding approach is what I'm doing, and now I'm trying to figure out a way to simulate promises (or something like that)

Currently I have something like this (using the socket wrapper that you wrote in an HTML5 blog -- thanks!)

]).controller('DefinitionAddCtrl', ['$scope', 'socket', ($scope, socket) ->
  definitionAddedHandler = (definitionAdded) ->
    console.log 'A definition was added:'
    console.log definitionAdded

  socket.handle 'DefinitionAdded', definitionAddedHandler

  $scope.definitionAdd = ->
    cmd = 
      word: $scope.word
      definition: $scope.definition
    socket.send 'DefinitionAdd', cmd

  $scope.$on '$destroy', ->
    socket.removeHandler 'DefinitionAdded', definitionAddedHandler
    console.log 'the definition add controller is being destroyed'
])

Partly, I believe that some of these things I'm doing do not / should not use socket.io -- they would be simpler as PUT / POST or GET -- but for now I'm just trying to work through ideas quickly, so I want a way to make it feel more abstract in the controller.

My adaption of your code currently looks like this:

  app.service 'socket', ['$rootScope', '$location', ($rootScope, $location) ->
    socket = io.connect("http://localhost:8000")
    handlerDeregistrationCallbacks = []

    socketWrapper =
      on: (topic, callback) ->
        socket.on topic, ->
          args = arguments
          $rootScope.$apply ->
            callback.apply socket, args

      emit: (topic, data, callback) ->
        socket.emit topic, data, ->
          args = arguments
          $rootScope.$apply ->
            callback.apply socket, args if callback

      send: (topic, data) ->
        console.log "send: " + topic + " -> " + data
        @emit "message", [topic, data]

      invoke: (messageClassName, message) ->
        console.log "recv: " + messageClassName + " -> "
        console.log message
        $rootScope.$broadcast messageClassName, message

      removeHandler: (topic, handler) ->
        for deregistrationCallback in handlerDeregistrationCallbacks
          if deregistrationCallback.topic is topic and deregistrationCallback.handler is handler
            console.log 'deregistering callback for topic: ' + topic
            deregistrationCallback.deregister()

      handle: (topic, handler) ->
        deregistrationCallback = $rootScope.$on topic, (event, message) ->
          handler message, $location, $rootScope
        handlerDeregistrationCallbacks.push 
          topic: topic
          handler: handler
          deregister: deregistrationCallback

I suppose I could have just returned the deregistrationCallback to the caller and had the $destroy handler call it directly instead of storing it and looking it up.

But, in any case, I want to make it feel like less work in the controller. I'm thinking of an API like this:

.controller('DefinitionAddCtrl', ['$scope', 'api', ($scope, api) ->
  $scope.definitionAdd
    cmd = _.pick $scope, 'word', 'definition'
  api.send 'DefinitionAdd', cmd, (err, definitionAdded) ->
    if err?
      # handle error somehow
    console.log 'A definition was added successfully:'
    console.log definitionAdded

  # Would be nice to not have to do this part:
  $scope.$on '$destroy', ->
    api.removeHandlerFor 'DefinitionAdd'
])

from angular-socket-io.

JogoShugh avatar JogoShugh commented on July 29, 2024

Here's an initial cut at what I was saying, based on ideas from both of you, sorry for the Coffee if you don't dig it :)

First, my modified socket service with some things specific to my needs like invoke and send, followed by an apiFactory which takes the current $scope as a parameter so that it can subscribe to $destroy and clean up its mess:

  app.service 'socket', ['$rootScope', '$location', ($rootScope, $location) ->
    socket = io.connect("http://localhost:8000")
    handlerDeregistrationCallbacks = []

    socketWrapper =
      on: (topic, callback) ->
        socket.on topic, ->
          args = arguments
          $rootScope.$apply ->
            callback.apply socket, args

      emit: (topic, data, callback) ->
        socket.emit topic, data, ->
          args = arguments
          $rootScope.$apply ->
            callback.apply socket, args if callback

      send: (topic, data) ->
        console.log "send: " + topic + " -> " + data
        @emit "message", [topic, data]

      invoke: (messageClassName, message) ->
        console.log "recv: " + messageClassName + " -> "
        console.log message
        $rootScope.$broadcast messageClassName, message

      handle: (topic, handler) ->
        deregistrationCallback = $rootScope.$on topic, (event, message) ->
          handler message, $location, $rootScope
        return deregistrationCallback

    socketWrapper
  ]
  app.factory 'apiFactory', ['$rootScope', 'socket', ($rootScope, socket) ->
    return (scope) ->
      handlerDeregistrationCallbacks = {}
      scope.$on '$destroy', ->
        for responseTopicName, deregistrationCallback of handlerDeregistrationCallbacks
          deregistrationCallback()
      obj = send: (topic, data, handler, responseTopicName='') ->
          if responseTopicName is ''
            responseTopicName = topic + 'Complete'
          if not handlerDeregistrationCallbacks[responseTopicName]?
            deregistrationCallback = socket.handle responseTopicName, handler
            handlerDeregistrationCallbacks[responseTopicName] = deregistrationCallback
          socket.send topic, data
      return obj
  ]

And, in my controller:

]).controller('DefinitionAddCtrl', ['$scope', 'apiFactory', ($scope, apiFactory) ->
  api = apiFactory $scope
  $scope.definitionAdd = ->
    cmd = 
      word: $scope.word
      definition: $scope.definition
    api.send 'DefinitionAdd', cmd, (definitionAdded) ->
      console.log 'A definition was added successfully:'
      console.log definitionAdded
])

This is vastly simpler than what I had before:

.controller('DefinitionAddCtrl', ['$scope', 'socket', ($scope, socket) ->
  definitionAddedHandler = (definitionAdded) ->
    console.log 'A definition was added:'
    console.log definitionAdded

  socket.handle 'DefinitionAdded', definitionAddedHandler

  $scope.definitionAdd = ->
    cmd = 
      word: $scope.word
      definition: $scope.definition
    socket.send 'DefinitionAdd', cmd

  $scope.$on '$destroy', ->
    socket.removeHandler 'DefinitionAdded', definitionAddedHandler
    console.log 'the definition add controller is being destroyed'
])

Hope this helps!

from angular-socket-io.

btford avatar btford commented on July 29, 2024

Thanks for the input, all.

I decided that this is the best way to handle it.
This method uses AngularJS's own event system and typically results in less boilerplate.

Feedback welcome.

from angular-socket-io.

cwagner22 avatar cwagner22 commented on July 29, 2024

I think the soltuion used by ngSocketIO ( .bindTo($scope) ) is more handy.

from angular-socket-io.

btford avatar btford commented on July 29, 2024

@jitowix it's trivial to implement this .bindTo($scope) feature as a decorator building upon this module.

from angular-socket-io.

arnars avatar arnars commented on July 29, 2024

So how do I actually use the removeListener? As of now I do like this:

    $scope.$on('$destroy', function() {
        mySocket.removeListener();
    });

and initiating them like this:

    mySocket.on('TEST', function(msg) {
            console.log(msg)
    });

from angular-socket-io.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.