5

Let's say i have two or more hubs in my server application. My javascipt client (Angular SPA) initialy needs a connection to the first hub, and needs to subscribe to a method like this:

connection = $.hubConnection(appSettings.serverPath);
firstHubProxy = connection.createHubProxy('firstHub');
firstHubProxy('eventFromFirstHub', function () {
        console.log('Method invokation from FirstHub');
            });
connection.start().done(function (data) {
                console.log("hub started");
            });

Everything is working fine. Now a user of my Angular SPA may decide to put a widget on his page, which needs to subcribe to a method from the second hub:

secondHubProxy = connection.createHubProxy('firstHub');
secondHubProxy('eventFromSecondHub', function () {
        console.log('Method invokation from SecondHub');
            });

The method from the second hub is not working. I guess because it was created after connection.start().

My example is simplified, in my real appplication there will be 20+ hubs to which users may or may not subscribe by adding or removing widgets to their page.

As far as i can tell i have two options:

  1. call connection.stop() and then connection.start(). Now both hub subscriptions are working. This just doesn't feel right, because on all hubs, the OnConnected() event fires, and my application will be starting and stopping all the time.
  2. create hub proxy objects for all possible hubs, subscribe to a dummy method on all possible hubs, so the application can subscibe to hub methods later if desired. This also doesn't feel right, because i need to create 20+ hub proxies, while i may need just a few of those.

Is anybody aware of a pattern which i can use to accomplish this? Or am i missing something very simple here?

Jeroen1984
  • 1,616
  • 1
  • 19
  • 32

1 Answers1

4

Personally I use #2. I have a hub service that subscribes to all client methods. Any of my other angular components then pull that hub service in and subscribe to its events as needed.

Here it is;

hub.js

(function () {
    'use strict';

    angular
        .module('app')
        .factory('hub', hub);

    hub.$inject = ['$timeout'];

    function hub($timeout) {
        var connection = $.connection.myHubName;

        var service = {
            connect: connect,
            server: connection.server,
            states: { connecting: 0, connected: 1, reconnecting: 2, na: 3, disconnected: 4 },
            state: 4
        };

        service = angular.extend(service, OnNotify());

        activate();

        return service;

        function activate() {
            connection.client.start = function (something) {
                service.notify("start", something);
            }

            connection.client.anotherMethod = function (p) {
                service.notify("anotherMethod", p);
            }

            // etc for all client methods 

            $.connection.hub.stateChanged(function (change) {
                $timeout(function () { service.state = change.newState; });
                if (change.state != service.states.connected) service.notify("disconnected");
                console.log("con:", _.invert(service.states)[change.oldState], ">", _.invert(service.states)[change.newState]);
            });

            connect();
        }

        function connect() {
                $.connection.hub.start({ transport: 'auto' });
        }
    }
})();

OnNotify

var OnNotify = function () {
    var callbacks = {};
    return {
        on: on,
        notify: notify
    };

    function on(name, callback) {
        if (!callbacks[name])
            callbacks[name] = [];
        callbacks[name].push(callback);
    };

    function notify(name, param) {
        angular.forEach(callbacks[name], function (callback) {
            callback(param);
        });
    };
}

Then I can subscribe to things as needed, for example in a controller;

(function () {
    'use strict';

    angular
        .module('app')
        .controller('MyController', MyController);

    MyController.$inject = ['hub'];

    function MyController(hub) {
        /* jshint validthis:true */
        var vm = this;
        vm.something = {};

        hub.on('start', function (something) {
            $timeout(function () {
                console.log(something);
                vm.something = something;
            });
        });
    }
})();
Stafford Williams
  • 9,696
  • 8
  • 50
  • 101
  • Thanks for your example. So this also means that you use only one Hub in your server application? – Jeroen1984 Dec 17 '15 at 08:52
  • 1
    Yes, but the same "subscribe to all client methods" applies. – Stafford Williams Dec 17 '15 at 08:54
  • Ok i get the point. I will customize it a little so initially only the connection gets started and the hub proxy gets created. That way i can let my controllers subscribe (.on) and unsubcribe (.off) to the desired hub events, withoud having to hard code all the possible client methods (like you do in your active() function) – Jeroen1984 Dec 17 '15 at 09:02
  • A little off-topic, but i see you use the angular.extend(). I was not aware of such functionality. Can you explain in a few words what it does and what the benefits are in this case? – Jeroen1984 Dec 17 '15 at 09:03
  • It's defined [here](https://docs.angularjs.org/api/ng/function/angular.extend), but I'm using so I can easily attach that `OnNotify` functionality to anything - rather than code it per controller/service. Allows me to easily communicate between controllers/services etc. – Stafford Williams Dec 17 '15 at 09:05
  • @Jeroen Can you explain me the procedure you followed so that only connection get started and hub proxy.I am facing similar problem as you faced and I don't want everything inside one hub as Williams suggested. – cauchy Mar 01 '16 at 12:40