0

I have an application reading some values from an external device. I'm using angularjs as my javascript framework. I'm using angular-ui for my routing.

I'm using the resolve to pass the web socket it to the controller, since it could be used in multiple screens

.state('dashboard.count', {
    parent: 'dashboard',
    url: "/lane/:laneID",
    templateUrl: '/app/count/count.html',
    controller: 'CountCtrl as vm',
    resolve: {
        websocket: ['webSocketFactory', function (webSocketFactory) {
            return webSocketFactory.websocket;
        }]
    }
});

The websocket is retrieved by a factory:

function webSocketFactory($log, wsUri) {
    var factory = {
        sendMessage: sendMessage,
        websocket: new WebSocket('myurl')
    }

    return factory;
    }
}

In my controller I have a simple calls to the websocket API

websocket.onopen = function () {
    $log.debug('open');
    webSocketFactory.sendMessage('send my message through the factory');
    vm.connecting = false;
}

Probably 70% of the time this works just fine vm.connecting gets set to false and I can use the web-socket. However, it's unpredictable.

I'm not real happy with the accepted answer on this SO Question, The one voted higher seems to be the pattern I'm following. Does anyone have any suggestions how to make this more predictable?

0

1 Answer 1

4

I suspect it's unreliable because the onopen callback would happen before your controller instance is ready. If I'm right about that, the following code should work, and also allow you to queue up a message to be sent as soon as the websocket connects.

Try adding an onopen function right after creating the new WebSocket(), and get the service to deal with all the inner workings of the socket rather than the controller.

function webSocketService($rootScope, $log, $q, wsUri) {

    var websocketConnectedDeferred, isConnected, websocket;

    var createNewWebSocket = function(url){
        websocketConnectedDeferred = $q.defer();
        isConnected = false;

        websocket = new WebSocket(url)
        websocket.onopen = onWebSocketOpen;
        websocket.onmessage = onWebSocketMessage;

        return websocket;
    };

    var onWebSocketOpen = function(){
        $log.debug('open');
        isConnected = true;
        websocketConnectedDeferred.resolve();
    };

    var onWebSocketMessage = function(incomingMessage){
        $rootScope.$broadcast('websocketMessageReceived', incomingMessage);
    };

    var sendMessage = function(message){
        websocketConnectedDeferred.promise.then(function(){
            websocket.doTheThing(message);
        });
    };

    var iswebsocketConnectedDeferred = function(){
        return isConnected;
    };

    var service = {
        sendMessage: sendMessage,
        websocket: createNewWebSocket('myUrl'),
        iswebsocketConnectedDeferred: iswebsocketConnectedDeferred
    };

    return service;
}

Controller:

$scope.$on('websocketMessageReceived', function(event, incomingMessage){
    // Do something with 'incomingMessage'
});

webSocketService.sendMessage('send my message through the factory');
Sign up to request clarification or add additional context in comments.

9 Comments

Think it is necessary to put anything in the controller resolve?
For the message to be sent, no, it will be sent by the service when the promise resolves. If you want the controller to do something after connecting is successful (display some 'Connected' message to the user or whatever), then add a method to return the websocketConnectedPromise.promise and respond to that. webSocketFactory.connected().then(function(){ $scope.connectedStatus = 'Connected!!!' });
How could you place the .onmesssage in the controller if the factory holds the socket?
Oh, I forgot to mention one other thing. If you make your webSocketFactory a service instead of a factory or resolving it as you've done in your example then you'll get the same instance in every controller and you won't need any of the state resolving code. myModule.service('WebSocetService, function($log, $q, wsUri){ ... });
You can still assign the onmessage function to the websocket like this: webSocketFactory.websocket.onmessages = function(){}; I still prefer to keep all that logic abstracted from the controller, so I'd use an event to communicate from the service. I'll update the answer with an example.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.