SignalR 客户端在 AngularJs 控制器中不起作用
SignalR client doesn't work inside AngularJs controller
我已经创建了 Angular 基于路由的应用程序。
在不使用路由的情况下,Signalr 工作正常,但是当我使用路由时,Signalr 只以一种方式工作——从客户端到服务器。
<html ng-app="SignalR">
<head>
//Scripts
<script src="~/signalr/hubs"></script>
</head>
<body>
<a href="#/Today">Today</a>
<a href="#/History">History</a>
<div ng-view>
</div>
<script type="text/javascript">
var hubConnetion = undefined;
hubConnetion = $.connection.hubs;
$.connection.hub.start();
var SignalRControllers = angular.module('SignalRControllers', []);
var SignalRApp = angular.module('SignalR', ['ngRoute', 'SignalRControllers']);
SignalRApp.config(['$routeProvider',
function ($routeProvider) {
$routeProvider.
when('/Today', {
templateUrl: '@Url.Action("Today","Home")'
}).
when('/History', {
templateUrl: '@Url.Action("History", "Home")'
})
}]);
</script>
<script src="~/AngularJs/TodayController.js"></script>
</body>
</html>
TodayController.js
SignalRControllers.controller('TodayController', ['$scope', '$http',
function ($scope, $http) {
$scope.sendTask = function () {
hubConnetion.server.sendTask($("#taskName").val());//works
}
$scope.Task = "task1";
hubConnetion.client.getTask= function (Task) {//never invoked
$scope.Task = Task;
}
}]);
Today.html
<div ng-controller="TodayController">
<div>{{Task}}</div>
<input id="taskName" type="text" />
<div ng-click="sendTask()">SendTask</div>
</div>
Hubs.cs
public class Hubs: Hub
{
public void SendTask(string Name)
{
Clients.All.GetTask(Name);
}
}
我不确定,但我认为因为 hubConnetion 变量包含在控制器中,所以 signlarR 找不到它。
有什么 angular 魔法可以解决这个问题吗?
我也是 SignalR 的新手(并且是 Angular 的第一次用户)所以我在 ASP.NET MVC 4 (SignalR 2.2) 中重新创建(并简化)了项目来确定问题 。而且我认为还有不止一个...
1) SignalR 正在使用 JQuery。在 jQuery(以及几乎任何其他 JS 框架)中,最好在 'document ready' 事件之后执行代码。因此,我将 SignalR 设置包装在 jQuery 文档就绪处理程序中(参见下面的代码)。
此外,我删除了 hubConnetion
全局变量,因为全局变量不好 + 在 jQuery 文档就绪处理程序中包装后,变量是该处理程序的本地变量,无论如何都无法在控制器代码中访问...
2) 恕我直言,主要问题是这个(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.
您的事件处理程序是在控制器工厂方法中设置的,该方法在连接 start()
方法之后被调用。
尝试在调用 start()
之前添加 temporary 处理程序,如下所示:
$.connection.tasklistHub.client.getTask = function () { };
3) 为确保问题出在 SignalR 而不是 Angular,我已打开服务器端和客户端 tracing。完成上述所有更改后,客户端成功接收到来自服务器的事件:
SignalR: Triggering client hub event 'getTask' on hub 'TasklistHub'
但是 Angular 无法用接收到的数据更新 div。解决方案是使用 this article about using SignalR and Angular together 中提到的 $rootScope.$apply
函数。现在一切都按预期工作。请参阅下面的完整代码。
旁注:我知道这只是示例代码。在任何现实世界的项目中,我都会考虑将 SignalR 集线器包装在客户端上的某种服务中,如上面提到的文章或使用 this library(看起来非常优雅)
我也收回我之前关于客户端处理程序名称的回答,因为根据同一文档,客户端方法名称匹配不区分大小写...
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;
});
};
}
]
);
您的解决方案是完美的,对于那些即使在您的讲座之后仍面临此问题的人,有一个简单的解决方案可以在应用程序的每个部分从服务器到客户端获取响应:
angular.module('groupApp').run(function ($rootScope, chat) {
$rootScope.messages = [];
chat.client.newMessage = function onNewMessage(message) {
$rootScope.messages.push({ message: message });
$rootScope.$apply();
};
});
而且在您看来,您可以在任何地方使用 'messages' 数组:
<div ng-repeat="m in messages">
{{m.message}}
</div>
My issue
我已经创建了 Angular 基于路由的应用程序。 在不使用路由的情况下,Signalr 工作正常,但是当我使用路由时,Signalr 只以一种方式工作——从客户端到服务器。
<html ng-app="SignalR">
<head>
//Scripts
<script src="~/signalr/hubs"></script>
</head>
<body>
<a href="#/Today">Today</a>
<a href="#/History">History</a>
<div ng-view>
</div>
<script type="text/javascript">
var hubConnetion = undefined;
hubConnetion = $.connection.hubs;
$.connection.hub.start();
var SignalRControllers = angular.module('SignalRControllers', []);
var SignalRApp = angular.module('SignalR', ['ngRoute', 'SignalRControllers']);
SignalRApp.config(['$routeProvider',
function ($routeProvider) {
$routeProvider.
when('/Today', {
templateUrl: '@Url.Action("Today","Home")'
}).
when('/History', {
templateUrl: '@Url.Action("History", "Home")'
})
}]);
</script>
<script src="~/AngularJs/TodayController.js"></script>
</body>
</html>
TodayController.js
SignalRControllers.controller('TodayController', ['$scope', '$http',
function ($scope, $http) {
$scope.sendTask = function () {
hubConnetion.server.sendTask($("#taskName").val());//works
}
$scope.Task = "task1";
hubConnetion.client.getTask= function (Task) {//never invoked
$scope.Task = Task;
}
}]);
Today.html
<div ng-controller="TodayController">
<div>{{Task}}</div>
<input id="taskName" type="text" />
<div ng-click="sendTask()">SendTask</div>
</div>
Hubs.cs
public class Hubs: Hub
{
public void SendTask(string Name)
{
Clients.All.GetTask(Name);
}
}
我不确定,但我认为因为 hubConnetion 变量包含在控制器中,所以 signlarR 找不到它。 有什么 angular 魔法可以解决这个问题吗?
我也是 SignalR 的新手(并且是 Angular 的第一次用户)所以我在 ASP.NET MVC 4 (SignalR 2.2) 中重新创建(并简化)了项目来确定问题 。而且我认为还有不止一个...
1) SignalR 正在使用 JQuery。在 jQuery(以及几乎任何其他 JS 框架)中,最好在 'document ready' 事件之后执行代码。因此,我将 SignalR 设置包装在 jQuery 文档就绪处理程序中(参见下面的代码)。
此外,我删除了 hubConnetion
全局变量,因为全局变量不好 + 在 jQuery 文档就绪处理程序中包装后,变量是该处理程序的本地变量,无论如何都无法在控制器代码中访问...
2) 恕我直言,主要问题是这个(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.
您的事件处理程序是在控制器工厂方法中设置的,该方法在连接 start()
方法之后被调用。
尝试在调用 start()
之前添加 temporary 处理程序,如下所示:
$.connection.tasklistHub.client.getTask = function () { };
3) 为确保问题出在 SignalR 而不是 Angular,我已打开服务器端和客户端 tracing。完成上述所有更改后,客户端成功接收到来自服务器的事件:
SignalR: Triggering client hub event 'getTask' on hub 'TasklistHub'
但是 Angular 无法用接收到的数据更新 div。解决方案是使用 this article about using SignalR and Angular together 中提到的 $rootScope.$apply
函数。现在一切都按预期工作。请参阅下面的完整代码。
旁注:我知道这只是示例代码。在任何现实世界的项目中,我都会考虑将 SignalR 集线器包装在客户端上的某种服务中,如上面提到的文章或使用 this library(看起来非常优雅)
我也收回我之前关于客户端处理程序名称的回答,因为根据同一文档,客户端方法名称匹配不区分大小写...
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;
});
};
}
]
);
您的解决方案是完美的,对于那些即使在您的讲座之后仍面临此问题的人,有一个简单的解决方案可以在应用程序的每个部分从服务器到客户端获取响应:
angular.module('groupApp').run(function ($rootScope, chat) {
$rootScope.messages = [];
chat.client.newMessage = function onNewMessage(message) {
$rootScope.messages.push({ message: message });
$rootScope.$apply();
};
});
而且在您看来,您可以在任何地方使用 'messages' 数组:
<div ng-repeat="m in messages">
{{m.message}}
</div>
My issue