Search code examples
angularjssignalrdashboardsignalr-hub

How to dynamically add hub to SignalR and have different scopes


I'm attempting to build a Dashboard with widgets (built as directives). I'd like to at a later stage have the ability to dynamically add widgets, but I'd only like the active widgets (and hubs) to receive data, thus if a widget isn't active i don't want the hub to be registered.

Eg for the duration of the user using the app there will be a global signalR context, as well as page specific ones, which will be spawned/destroyed as needed.

enter image description here

This is my best try ATM... which isn't working

factory

   (function () {
    'use strict';

    angular.module('app').factory('hubFactory', ['permissionSvc', 'common', hubFactory]);

    function hubFactory(permissionSvc, common) {

        var connection = [];

        return {
            context: function (name) {

                if (!connection[name]) {
                    var conn = $.connection;
                    conn.hub.url = common.serviceUrl + '/signalr';
                    conn.hub.start();

                    conn.prototype.addHub = function (hubName, options) {

                        // defaults
                        var opts = {
                            authorize: true
                        };

                        angular.extend(opts, options);

                        if (opts.authorize) {
                            permissionSvc.createPrefilter();
                        }

                        var hub = conn[hubName];

                        var run = function () {
                            hub.server.initialise();
                        };

                        return {
                            hub: hub,
                            run: run
                        };
                    };

                    connection[name] = conn;
                }

                return connection[name]();
            }
        };
    }
})();

widget directive-controller

 controller: function ($scope) {               

               var context = hubFactory.context('dashboard');

               var instance = context.addHub('agreementApprovalsHub');

               instance.hub.client.getAllUnapprovedAgreements = function (data) {

                    $scope.data = data;
                };

               instance.run();
            } 

The following needs to be called in the done method of the start... but what if i want to, start up the connection on page load, and then append hubs as needed (or is my thinking wrong?)

There are various problems:

var run = function () { hub.server.initialise(); };

vs

var run = function () { conn.hub.start().done(function() { hub.server.initialise(); }); };

TBH, it feels like i'm butchering the code, and need to probably start from scratch at this stage.. I'm thoroughly confused on how to go about it all, and if its even possible.


Solution

  • I think I have a more elegant solution.

    Service

     app.factory("signalrFactory", function ($rootScope, $http, $timeout) {
        var factory = {};
        factory.connection = $.connection;
    
        var startDoneFunctions = [];
        var debounce;
    
        factory.start = function (done) {
            factory.connection.hub.stop();
    
            if (done) {
                if (startDoneFunctions.indexOf(done) == -1) {
                    startDoneFunctions.push(done);
                }
            }
            if (debounce) $timeout.cancel(debounce);
            debounce = $timeout(function () {
                factory.connection.hub.start().done(function () {
                    for (var x = 0; x < startDoneFunctions.length; x++) {
                        startDoneFunctions[x]();
                    }
                });
            }, 100);
        };
    
        return factory;
    });
    

    Use

    controller('customerSummary', function ($scope, signalrFactory) {
            $scope.customerHub = signalrFactory.connection.customerHub;
            $scope.customer;
            $scope.beingEditedText = "";
    
    
    
            $scope.customerHub.client.beingEdited = function (text) {
                $scope.beingEditedText = text;
            };
            $scope.customerHub.client.doneEditing = function () {
                $scope.beingEditedText = "";
            };
    
            $scope.customerHub.client.updateCustomer = function (customer) {
                $scope.customer = customer;
                $scope.$apply();
            };
    
            $scope.init = function (id) {
                signalrFactory.start(function () {
                    $scope.customerHub.server.getCustomer(id);
    
                });
            };
        });
    

    What this way allows is to add when needed and not having to add each hub at bootstrap. I am now able to have different done() methods from anywhere.