I'm also fairly new to SignalR (and first time user of Angular) so I'v recreated (and simplified) the project in ASP.NET MVC 4 (SignalR 2.2) to nail the problem . And I think there is more then one...
1) SignalR is using JQuery. In jQuery (and almost any other JS framework) its relly better to execute your code after 'document ready' event. So I wrapped SignalR setup in jQuery document ready handler (see code below).
Additionally I removed hubConnetion
global variable because global variables are bad + after wrapping in jQuery document ready handler, variable is local to that handler and not accessible in controller code anyway...
2) Main problem IMHO is this (part of SignalR client API):
Normally you register event handlers before calling the start method
to establish the connection. If you want to register some event
handlers after establishing the connection, you can do that, but you
must register at least one of your event handler(s) before calling the
start method. One reason for this is that there can be many Hubs in an
application, but you wouldn't want to trigger the OnConnected event on
every Hub if you are only going to use to one of them. When the
connection is established, the presence of a client method on a Hub's
proxy is what tells SignalR to trigger the OnConnected event. If you
don't register any event handlers before calling the start method, you
will be able to invoke methods on the Hub, but the Hub's OnConnected
method won't be called and no client methods will be invoked from the
server.
Your event handler is setup in controller factory method which is called much later then connection start()
method.
Try to add temporary handler before calling start()
like this:
$.connection.tasklistHub.client.getTask = function () { };
3) To be sure the problem is in SignalR and not Angular, i'v turned on both server and client side tracing. After all the changes above, event from server was successfully received on client:
SignalR: Triggering client hub event 'getTask' on hub 'TasklistHub'
But Angular failed to update div with data received. Solution is to use $rootScope.$apply
function mentioned in this article about using SignalR and Angular together. Now everything works as expected. See full code below.
Sidenote: I understand this is just sample code. In any real world project I would consider wrapping SignalR hubs on client in some kind of service like in article mentioned above or using this library (looks very elegant)
I also take back my previous answer regarding the client side handler name as according to the same document, client side method name matching is case-insensitive...
Index.cshtml
<html lang="en" ng-app="SignalR">
<head>
<meta charset="utf-8" />
<title>SignalR & AngularJS</title>
<script src="~/Scripts/jquery-1.6.4.js"></script>
<script src="~/Scripts/jquery.signalR-2.2.0.js"></script>
<script src="~/signalr/hubs"></script>
<script src="~/Scripts/angular.js"></script>
<script type="text/javascript">
$(function() {
$.connection.hub.logging = true;
// temporary handler to force SignalR to subscribe to server events
$.connection.tasklistHub.client.getTask = function () { };
$.connection.hub.start().done(function () {
$('#connectionStatus').text('Connected');
console.log('Now connected, connection ID =' + $.connection.hub.id);
});
});
var SignalRControllers = angular.module('SignalRControllers', []);
var SignalRApp = angular.module('SignalR', ['SignalRControllers']);
</script>
<script src="~/Scripts/app/TodayController.js"></script>
</head>
<body>
<div id="body">
<div ng-view>
<div ng-controller="TodayController">
<div id="connectionStatus"></div>
<div>{{Task}}</div>
<input id="taskName" type="text"/>
<input type="button" name="Send Task" value="Send Task" data-ng-click="sendTask()" />
</div>
</div>
</div>
</body>
</html>
TodayController.js
SignalRControllers.controller('TodayController', [
'$scope', '$http', '$rootScope',
function ($scope, $http, $rootScope) {
$scope.sendTask = function() {
$.connection.tasklistHub.server.sendTask($("#taskName").val());
};
$scope.Task = "Empty";
$.connection.tasklistHub.client.getTask = function (task) {
console.log(task);
$rootScope.$apply(function () {
$scope.Task = task;
});
};
}
]
);