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

时间:2015-12-22 22:11:14

标签: javascript angularjs node.js socket.io

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

它有多个页面,如主页,登录/注册页面等。

我想实施一个"聊天"您可以使用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控制器声明的函数仍然可以被调用,但是&#34; $ scope.message&#34;值(包含被输入的消息)始终为空。

这是我的聊天控制器(实际上称为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>

按下&#34;发送&#34;按钮,AngularJS成功调用sendMessage函数,但检索&#34;消息&#34; value作为空字符串,导致它发送一个空的socket.io消息。

我对AngularJS很新,所以我的方法可能完全荒谬。我确信我错过了一些明显的东西,但在重新阅读文档之后,我似乎无法找到什么。

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

提前感谢您的帮助。

2 个答案:

答案 0 :(得分:1)

这不是关于你的问题,但我看到了我怀疑是错的东西。 当你使用另一个带有angularjs的库时,你应该使用它的桥(例如angular-socket-io)。

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

在您的代码中:

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

有一个问题:&#34; socket&#34;不是包含在angularjs中的库,所以当调用回调时,你的&#34; $ scope&#34;修改并未正确注意到angularjs。

你必须使用$ scope。$ apply(function(){code here修改$ scope});

示例:

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

编辑:

  

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

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

编辑2:

实际上它应该可以解决你的问题;)

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

答案 1 :(得分:1)

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