在 AngularJS 中使用范围和控制器时发生冲突

Conflicts when working with scopes and controllers in AngularJS

我有一个使用 AngularJS 和 NodeJS 后端的简单网站。

它有多个页面,如主页、login/register 页面等

我想实现一个 "Chat" 页面,您可以在其中使用 socket.io 向其他客户端发送消息。我已经让那部分工作了,使用本地控制器(本地,我的意思是在单个页面上激活 - 聊天页面)。

问题是,我希望聊天系统是全局的(即客户端可以在主页上接收消息,但它们仍然只会在返回聊天页面时显示)。


我在设置全局聊天控制器(在所有页面上处于活动状态)时遇到问题。

以下是我的收录方式:

<body ng-controller="AppCtrl"> <!-- include main controller -->
    <div ng-include="'header.tpl.html'"></div>
    <div ng-controller="ChatCtrl" class="page"> <!-- include global Chat controller -->
        <div ng-view class="container"></div>
    </div>
    <div ng-include="'footer.tpl.html'"></div>
    <!-- ...etc. -->
</body>

这工作得很好,但我似乎无法从我的聊天页面访问值。从 Chat 控制器声明的函数仍然可以调用,但“$scope.message”值(包含正在键入的消息)始终为空。


这是我的聊天控制器(实际上叫做 TravelCtrl)

angular.module('base').controller('TravelCtrl', //['$scope', 'security',
  function($rootScope, $scope, security, NgMap, $geolocation, socket){

    $scope.messages = [];

    // Socket listeners
    // ================

    socket.on('init', function (data) {
      $scope.name = data.name;
      $scope.users = data.users;
    });

    socket.on('send:message', function (message) {
      $scope.messages.push(message);
    });

    socket.on('change:name', function (data) {
      changeName(data.oldName, data.newName);
    });

    socket.on('user:join', function (data) {
      $scope.messages.push({
        user: 'Server',
        text: 'User ' + data.name + ' has joined.'
      });
      $scope.users.push(data.name);
    });

    // add a message to the conversation when a user disconnects or leaves the room
    socket.on('user:left', function (data) {
      $scope.messages.push({
        user: 'chatroom',
        text: 'User ' + data.name + ' has left.'
      });
      var i, user;
      for (i = 0; i < $scope.users.length; i++) {
        user = $scope.users[i];
        if (user === data.name) {
          $scope.users.splice(i, 1);
          break;
        }
      }
    });

    // Private helpers
    // ===============

    var changeName = function (oldName, newName) {
      // rename user in list of users
      var i;
      for (i = 0; i < $scope.users.length; i++) {
        if ($scope.users[i] === oldName) {
          $scope.users[i] = newName;
        }
      }

      $scope.messages.push({
        user: 'Server',
        text: 'User ' + oldName + ' has been authenticated as ' + newName + '.'
      });
    }

    // Methods published to the scope
    // ==============================

    $scope.changeName = function () {
      socket.emit('change:name', {
        name: $scope.newName
      }, function (result) {
        if (!result) {
          alert('There was an error changing your name');
        } else {

          changeName($scope.name, $scope.newName);

          $scope.name = $scope.newName;
          $scope.newName = '';
        }
      });
    };

    $scope.sendMessage = function () {

      socket.emit('send:message', {
        message: $scope.message
      });

      // add the message to our model locally
      $scope.messages.push({
        user: $scope.name,
        text: $scope.message
      });

      // clear message box
      $scope.message = '';

    };

    // ================

    var init = function () {
      $scope.newName = security.currentUser.username;
      $scope.changeName();
    }

    if ($rootScope.hasLoaded() && $scope.name != security.currentUser.username) {
      init();
    } else {
      $rootScope.$on('info-loaded', init);
    }

  }
  //]
);

以及聊天页面本身。奇怪的是,连接的用户和消息显示正确,但控制器似乎无法检索键入的消息。

<div class='col'>
  <h3>Users</h3>
  <div class='overflowable'>
    <p ng-repeat='user in users'>{{user}}</p>
    </div>
</div>

<div class='col'>
    <h3>Messages</h3>
    <div class='overflowable'>
    <p ng-repeat='message in messages' ng-class='{alert: message.user == "chatroom"}'>{{message.user}}: {{message.text}}</p>
    </div>
</div>

<div class='clr'>
  <form ng-submit='sendMessage()'>
    Message: {{message}}<br/>
    <input size='60', ng-model='message'/>
    <input type='submit', value='Send as {{name}}'/>
    </form>
</div>

当按下 "Send" 按钮时,AngularJS 成功调用了 sendMessage 函数,但检索到的 "message" 值是一个空字符串,导致它发送一个空的 socket.io 消息。


我是 AngularJS 的新手,所以我的方法可能完全荒谬。我确信我遗漏了一些明显的东西,但在再次阅读文档后,我似乎真的找不到什么。

这是组织 AngularJS 应用程序的正确方法吗?

在此先感谢您的帮助。

这与你的问题无关,但我看到了一些我怀疑不对的地方。 当你使用另一个带有 angularjs 的库时,你应该使用一个桥接它(例如 angular-socket-io)。

当您使用 angular 执行 $http 调用时,它会在回调中正确更新 $scope,并且会在视图中看到更改。

在您的代码中:

socket.on('send:message', function (message) {
  $scope.messages.push(message);
});

有一个问题:"socket" 不是 angularjs 中包含的库,因此当回调被调用时,您的“$scope”修改没有被正确注意到 angularjs.

你必须使用 $scope.$apply(function() { code here which modifies $scope });

示例:

socket.on('send:message', function (message) {
  $scope.$apply(function() {
    $scope.messages.push(message);
  });
});

编辑:

I would like the chat system to be global (i.e. client can receive messages while being on the homepage, but they'll still only be displayed when going back on the Chat page).

要么将数据存储在全局变量中,要么使用 $rootScope,它是您在应用程序中使用的所有 $scope 的父级作用域。

编辑 2:

事实上它应该可以解决您的问题 ;)

另外一件事: 1)对全局变量(或全局变量)使用 $rootScope 而不是 $scope 。在任何 $scope 中,您将访问 $rootScope 变量($scope 是 $rooScope 或父 $scope 的副本)。 2) 只注册一次socket.io。目前,如果您更改页面,您将在每次页面更改时注册新的回调。

最近构建了一个大型 Angular/Socket.IO 应用程序,我强烈建议您将所有 Socket 实现放入服务中。该服务将维护您所有的套接字状态,并允许您将其注入任何所需的控制器。这将允许您拥有聊天的主页,但仍然能够在应用程序的其他区域显示通知、聊天用户信息等。