Написание AngularJS App с Socket.IO

Написание AngularJS App с Socket.IO

AngularJS  это огромная структура JavaScript, которая дает нам два способа связывания данных, AngularJS быстрый и простой в использовании, мощная системная директива, которая позволяет создавать нам многоразовые пользовательские компоненты, и многое другое. Socket.IO является кросс-браузерной оболочкой  Polyfill для WebSockets , который делает разработку приложений в реальном времени на одном дыхании. Кстати, они работают достаточно хорошо вместе!

В этой статье о том, как интегрировать Socket.IO и добавить возможности приложения AngularJS, пример приложения для мгновенного обмена сообщениями (чат).

Скачать  AngularJS.

Написание AngularJS App с Socket.IO

Вы можете клонировать узел seed с Github:



git clone git://github.com/btford/angular-socket-io-seed my-project


или  скачать ZIP .

Если у вас есть seed, вы должны установить несколько свойств с НПМ. Откройте терминал, и выполните команду:



npm install


С помощью этих свойств, вы можете запустить приложение:



node app.js


и посмотреть его в браузере http://localhost:3000 что бы убедится что всё работает, как нужно.

Возможности APP

Есть много различных способов написать приложение для чата, я напишу в минимальной комплектации. Там будет только один чат, где все пользователи имеют право оставить сообщение. Смогут выбрать и изменить свое имя, но и имена должны быть уникальными. Сервер будет применять эту уникальность и сообщать, когда пользователи меняют свои имена. Пользователю должен предоставится список сообщений и список пользователей находящихся в данный момент в чате.

Интерфейс чата

Можно сделать простой интерфейс Jade, где provies необходимые элементы пользовательского интерфейса. Откройте views/index.jade и добавьте block body :



div(ng-controller='AppCtrl')
  .col
  h3 Messages
  .overflowable
  p(ng-repeat='message in messages') :

  .col
  h3 Users
  .overflowable
  p(ng-repeat='user in users')

  .clr
  form(ng-submit='sendMessage()')
  | Message:
  input(size='60', ng-model='message')
  input(type='submit', value='Send')

  .clr
  h3 Change your name
  p Your current user name is
  form(ng-submit='changeName()')
  input(ng-model='newName')
  input(type='submit', value='Change Name')


Открываем public/css/app.css и добавьте стили CSS:



/* app css stylesheet */

  .overflowable {
  height: 240px;
  overflow-y: auto;
  border: 1px solid #000;
  }

  .overflowable p {
  margin: 0;
  }

  /* poor man's grid system */
  .col {
  float: left;
  width: 350px;
  }

  .clr {
  clear: both;
  }


Взаимодействие с SOCKET.IO

Хотя Socket.IO для ввода-вывода переменных в окне, лучше всего, вставить его в AngularJS.

Мы начнём с написания кода для переноса объекта socket  на Socket.IO. Откройте public/js/services.js и замените содержимое:



app.factory('socket', function ($rootScope) {
  var socket = io.connect();
  return {
    on: function (eventName, callback) {
      socket.on(eventName, function () {
        var args = arguments;
        $rootScope.$apply(function () {
          callback.apply(socket, args);
        });
      });
    },
    emit: function (eventName, data, callback) {
      socket.emit(eventName, data, function () {
        var args = arguments;
        $rootScope.$apply(function () {
          if (callback) {
            callback.apply(socket, args);
          }
        });
      })
    }
  };
});


Обратите внимание, что мы вставили каждый socket обратного вызова в $scope.$apply . Это необходимо, чтобы проверить состояние приложения и обновления шаблонов, если произошли изменения после выполнения обратного вызова, переданные AngularJS. Внутри,  $http работает после некоторых запросов XHR, он вызывает $scope.$apply , так что AngularJS обновит свой вид, соответственно.

Это свойство не захватит весь Socket.IO API (что осталось в качестве упражнения для читателя). Тем не менее, он охватывает методы, используемые в данном руководстве, и должны направить вас в правильном направлении. Я могу вернуться к написанию с полным захватом, но это выходит за рамки этого руководства.

Сейчас, в наш контроллер, мы можем добавить объект для socket, так же как и с http:$



function AppCtrl($scope, socket) {
  /* Controller logic */
}


Внутри контроллера, добавим логику для отправки и получения сообщений. Открываем  js/public/controllers.js  и заменим содержимое следующим:



function AppCtrl($scope, socket) {

  // 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: 'chatroom',
      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: 'chatroom',
      text: 'User ' + oldName + ' is now known 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 = '';
  };
}


Это приложение будет имеет один вид, так что мы можем удалить маршрут с  public/js/app.js  и упростить его по адресу:



// Declare app level module which depends on filters, and services
var app = angular.module('myApp', ['myApp.filters', 'myApp.directives']);


Написание для сервера

Открываем  routes/socket.js . Нам необходимо определить объект для отслеживания состояния сервера,чтобы имена пользователей были уникальными.



/ Keep track of which names are used so that there are no duplicates
  var userNames = (function () {
  var names = {};

  var claim = function (name) {
  if (!name || userNames[name]) {
  return false;
  } else {
  userNames[name] = true;
  return true;
  }
  };

  // find the lowest unused "guest" name and claim it
  var getGuestName = function () {
  var name,
  nextUserId = 1;

  do {
  name = 'Guest ' + nextUserId;
  nextUserId += 1;
  } while (!claim(name));

  return name;
  };

  // serialize claimed names as an array
  var get = function () {
  var res = [];
  for (user in userNames) {
  res.push(user);
  }

  return res;
  };

  var free = function (name) {
  if (userNames[name]) {
  delete userNames[name];
  }
  };

  return {
  claim: claim,
  free: free,
  get: get,
  getGuestName: getGuestName
  };
}());


В основном это определяет набор имен, но с API имеет большой смысл для чат-сервера. Давайте это подключим к серверу для ответов вызова пользователя:



// export function for listening to the socket
  module.exports = function (socket) {
  var name = userNames.getGuestName();

  // send the new user their name and a list of users
  socket.emit('init', {
  name: name,
  users: userNames.get()
  });

  // notify other clients that a new user has joined
  socket.broadcast.emit('user:join', {
  name: name
  });

  // broadcast a user's message to other users
  socket.on('send:message', function (data) {
  socket.broadcast.emit('send:message', {
  user: name,
  text: data.message
  });
  });

  // validate a user's name change, and broadcast it on success
  socket.on('change:name', function (data, fn) {
  if (userNames.claim(data.name)) {
  var oldName = name;
  userNames.free(oldName);

  name = data.name;

  socket.broadcast.emit('change:name', {
  oldName: oldName,
  newName: name
  });

  fn(true);
  } else {
  fn(false);
  }
  });

  // clean up when a user leaves, and broadcast it to other users
  socket.on('disconnect', function () {
  socket.broadcast.emit('user:left', {
  name: name
  });
  userNames.free(name);
  });
};


Это, приложение должно быть полным. Попробовать его на работающем узле app.js . Запрос необходимо обновлять в режиме реального времени, благодаря Socket.IO.

Заключение

Еще много можно добавить в приложение обмена сообщениями. Например можно отправить пустое сообщение. Вы можете использовать ng-valid, чтобы предотвратить это поведение на стороне пользователя, а также проверить на сервере. Сервер может поддерживать новейшую историю сообщений в пользу подключенных новых пользователей к чату.

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *


3 + = 12

Можно использовать следующие HTML -теги и атрибуты: <a href= http://pixelcom.crimea.ua/"" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>