SignalR客户端在AngularJs控制器中不起作用

时间:2015-05-27 07:43:17

标签: javascript asp.net angularjs signalr signalr.client

我已根据路线创建了Angular应用程序。 不使用路由,信号器工作正常,但当我使用路由时,信号器只以一种方式工作 - 从客户端到服务器。

<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无法找到它。 是否有任何角度魔法可以解决这个问题?

2 个答案:

答案 0 :(得分:2)

我对SignalR(以及Angular的第一次使用者)也相当新,所以我在ASP.NET MVC 4(SignalR 2.2)中重新创建(并简化)了该​​项目以解决问题。而且我认为还有一个......

1)SignalR正在使用JQuery。在jQuery(以及几乎任何其他JS框架)中,在文档准备就绪之后执行代码会更好。&#39;事件。所以我在jQuery文档就绪处理程序中包装了SignalR设置(参见下面的代码)。 另外我删除了hubConnetion全局变量,因为全局变量在包装到jQuery文档就绪处理程序之后是坏的,变量是该处理程序的本地变量而且无论如何都无法在控制器代码中访问...

2)主要问题恕我直言SignalR client API的一部分):

  

通常在调用start方法之前注册事件处理程序   建立连接。如果你想注册一些活动   建立连接后的处理程序,你可以这样做,但你   必须在调用之前注册至少一个事件处理程序   启动方法。其中一个原因是一个中心可能有很多   应用程序,但您不希望触发OnConnected事件   如果您只使用其中一个,那么每个Hub。当。。。的时候   建立连接,在Hub上存在客户端方法   proxy是告诉SignalR触发OnConnected事件的东西。 如果你   在调用start方法之前,不要注册任何事件处理程序   将能够调用Hub上的方法,但Hub可以调用OnConnected   方法不会被调用,并且不会从中调用任何客户端方法   服务器

您的事件处理程序是在控制器工厂方法中设置的,稍后调用连接start()方法。 尝试在调用start()之前添加临时处理程序:

$.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;
                });             
            };
        }
    ]
);

答案 1 :(得分:0)

您的解决方案非常完美,即使在您的讲座之后,对于那些面临此问题的人来说,有一个简单的解决方案可以在应用的每个部分从服务器到客户端获得响应:

    angular.module('groupApp').run(function ($rootScope, chat) {
        $rootScope.messages = [];
        chat.client.newMessage = function onNewMessage(message) {
           $rootScope.messages.push({ message: message });
           $rootScope.$apply();
    };
});

在您看来,您可以使用&#39;消息&#39;阵列无处不在:

 <div ng-repeat="m in messages">
        {{m.message}}
 </div>

My issue