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

[英]Conflicts when working with scopes and controllers in AngularJS


I have a simple website that uses AngularJS with a NodeJS backend.

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

It has multiple pages, like a homepage, a login/register page, etc.

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

I'd like to implement a "Chat" page where you could send messages to other clients using socket.io. I already got that part working, using a local controller (by local, I mean active on a single page - the Chat page).

我想实现一个“聊天”页面,您可以使用socket.io向其他客户端发送消息。我已经使用本地控制器(通过本地,我的意思是在单个页面上活动 - 聊天页面)使该部分正常工作。

The problem is, 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).

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


I'm having an issue when setting the Chat controller global (active on all pages).

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

Here's how I'm including it:

这是我包括它的方式:

<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>

This works pretty well, but it seems like I can't access a value from my Chat page, though. Functions declared from the Chat controller can still be called, but the "$scope.message" value (which contains the message that's being typed) is always empty.

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


Here's my Chat controller (which is actually called TravelCtrl)

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

  }
  //]
);

As well as the Chat page itself. The strange thing is that connected users and messages display correctly, but the controller can't seem to retrieve the typed message.

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

<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>

When pressing the "Send" button, AngularJS successfully calls the sendMessage function, but retrieves the "message" value as an empty string, leading it to send an empty socket.io message.

按下“发送”按钮时,AngularJS成功调用sendMessage函数,但将“message”值检索为空字符串,从而导致它发送一个空的socket.io消息。


I'm quite new to AngularJS, so my approach might be totally ridiculous. I'm convinced I'm missing something obvious but after re-reading the docs again, I really can't seem to find what.

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

Is this a proper way to organise an AngularJS app?

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

Thanks in advance for your help.

在此先感谢您的帮助。

2 个解决方案

#1


1  

Having recently built a large scale Angular/Socket.IO application, I strongly suggest that you put all of your Socket implementation into a Service. This service will maintain all of your socket state, and allow you to inject it into any required controllers. This will allow you to have a main page for Chat, however still be able to display notifications, chat user information, etc in other areas of your application.

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

#2


1  

It's not about your problem, but I saw something I suspect to be wrong. When you use another library with angularjs, you should use a bridge to it (angular-socket-io for example).

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

When you do an $http call with angular, it updates $scope correctly in the callback and the changes are seen in the view.

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

In your code:

在你的代码中:

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

There is a problem: "socket" isn't a library included in angularjs, so when the callback is called, your "$scope" modification isn't correctly noticed to angularjs.

有一个问题:“socket”不是angularjs中包含的库,所以当调用回调时,你的“$ scope”修改没有正确地注意到angularjs。

You have to do use $scope.$apply(function() { code here which modifies $scope });

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

Example:

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

EDIT:

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).

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

Either store the datas in a global variable, or use $rootScope which is the parent scope of all the $scope you use in the application.

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

EDIT 2:

In fact it should solve your problem ;)

事实上它应该解决你的问题;)

Another things: 1) use $rootScope instead of $scope for global variables (or a global variable). In any $scope you will access $rootScope variables ($scope is a copy of either $rooScope or a parent $scope). 2) register socket.io only once. Currently, if you change pages, you will register new callbacks at EACH page change.

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


注意!

本站翻译的文章,版权归属于本站,未经许可禁止转摘,转摘请注明本文地址:https://www.itdaan.com/blog/2015/12/22/190d1274cf682243bafacd9b2db92ac1.html



 
  © 2014-2022 ITdaan.com 联系我们: