Socket.IO দিয়ে একটি AngularJS অ্যাপ লেখা

ভূমিকা

AngularJS হল একটি দুর্দান্ত জাভাস্ক্রিপ্ট ফ্রেমওয়ার্ক যা আপনাকে দ্বি-মুখী ডেটা বাইন্ডিং দেয় যা ব্যবহার করা সহজ এবং দ্রুত, একটি শক্তিশালী নির্দেশিক সিস্টেম যা আপনাকে পুনরায় ব্যবহারযোগ্য কাস্টম উপাদান তৈরি করতে এবং আরও অনেক কিছু ব্যবহার করতে দেয়। Socket.IO হল ওয়েবসকেটের জন্য একটি ক্রস-ব্রাউজার র‍্যাপার এবং পলিফিল যা রিয়েল-টাইম অ্যাপ্লিকেশনগুলিকে একটি হাওয়া তৈরি করে। প্রসঙ্গত, দুজনে একসঙ্গে বেশ ভালো কাজ করেন!

আমি আগে এক্সপ্রেসের সাথে একটি অ্যাঙ্গুলারজেএস অ্যাপ লেখার বিষয়ে লিখেছি, তবে এবার আমি অ্যাঙ্গুলারজেএস অ্যাপ্লিকেশনটিতে রিয়েল-টাইম বৈশিষ্ট্য যুক্ত করতে Socket.IO সংহত করার বিষয়ে লিখব। এই টিউটোরিয়ালে, আমি একটি তাত্ক্ষণিক বার্তাপ্রেরণ অ্যাপ লিখতে যাচ্ছি। এটি আমার আগের টিউটোরিয়াল (সার্ভারে একটি অনুরূপ node.js স্ট্যাক ব্যবহার করে) তৈরি করে, তাই আমি সুপারিশ করছি যে আপনি যদি Node.js বা Express এর সাথে পরিচিত না হন তবে প্রথমে এটি পরীক্ষা করে দেখুন।

ডেমো খুলুন

সর্বদা হিসাবে, আপনি Github এ সমাপ্ত পণ্য পেতে পারেন।

পূর্বশর্ত

Socket.IO সেট আপ এবং এক্সপ্রেসের সাথে একত্রিত করার জন্য কিছুটা বয়লারপ্লেট আছে, তাই আমি Angular Socket.IO Seed তৈরি করেছি।

শুরু করার জন্য, আপনি হয় Github থেকে কৌণিক-নোড-বীজ রেপো ক্লোন করতে পারেন:

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

অথবা জিপ হিসেবে ডাউনলোড করুন

একবার আপনার বীজ হয়ে গেলে, আপনাকে npm এর সাথে কয়েকটি নির্ভরতা ধরতে হবে। বীজ সহ ডিরেক্টরিতে একটি টার্মিনাল খুলুন এবং চালান:

npm install

এই নির্ভরতাগুলি ইনস্টল করার সাথে, আপনি কঙ্কাল অ্যাপটি চালাতে পারেন:

node app.js

এবং এটি আপনার ব্রাউজারে http://localhost:3000 দেখুন যাতে বীজটি আশানুরূপ কাজ করছে তা নিশ্চিত করতে।

অ্যাপের বৈশিষ্ট্য সম্পর্কে সিদ্ধান্ত নেওয়া

একটি চ্যাট অ্যাপ্লিকেশন লেখার জন্য কয়েকটি ভিন্ন উপায় রয়েছে, তাই আসুন আমাদের ন্যূনতম বৈশিষ্ট্যগুলি বর্ণনা করি যা আমাদের থাকবে৷ শুধুমাত্র একটি চ্যাট রুম থাকবে যেটির সকল ব্যবহারকারী থাকবে। ব্যবহারকারীরা তাদের নাম চয়ন এবং পরিবর্তন করতে পারেন, তবে নামগুলি অবশ্যই অনন্য হতে হবে। সার্ভার এই স্বতন্ত্রতা প্রয়োগ করবে এবং ব্যবহারকারীরা তাদের নাম পরিবর্তন করার সময় ঘোষণা করবে। ক্লায়েন্টকে বার্তাগুলির একটি তালিকা এবং বর্তমানে চ্যাট রুমে থাকা ব্যবহারকারীদের একটি তালিকা প্রকাশ করা উচিত।

একটি সহজ সামনে শেষ

এই স্পেসিফিকেশনের সাহায্যে, আমরা Jade এর সাথে একটি সাধারণ ফ্রন্ট এন্ড তৈরি করতে পারি যা প্রয়োজনীয় UI উপাদানগুলি প্রদান করে। 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 window একটি io ভেরিয়েবল প্রকাশ করে, এটি AngularJS এর ​​ডিপেন্ডেন্সি ইনজেকশন সিস্টেমে এনক্যাপসুলেট করা ভাল। সুতরাং, আমরা Socket.IO দ্বারা ফেরত socket অবজেক্ট মোড়ানোর জন্য একটি পরিষেবা লিখে শুরু করব। এটি দুর্দান্ত, কারণ এটি পরবর্তীতে আমাদের নিয়ামক পরীক্ষা করা আরও সহজ করে তুলবে। 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);
          }
        });
      })
    }
  };
});

লক্ষ্য করুন যে আমরা প্রতিটি সকেট কলব্যাক $scope.$apply এ মোড়ানো। এটি অ্যাঙ্গুলারজেএসকে বলে যে এটিকে আবেদনের অবস্থা পরীক্ষা করতে হবে এবং টেমপ্লেটগুলি আপডেট করতে হবে যদি এটিতে পাস করা কলব্যাক চালানোর পরে কোনও পরিবর্তন হয়। অভ্যন্তরীণভাবে, $http একইভাবে কাজ করে; কিছু XHR রিটার্ন করার পরে, এটি $scope.$apply কল করে, যাতে AngularJS সেই অনুযায়ী তার মতামত আপডেট করতে পারে।

মনে রাখবেন যে এই পরিষেবাটি সম্পূর্ণ Socket.IO API (এটি পাঠকের জন্য একটি অনুশীলন হিসাবে রেখে দেওয়া হয়েছে ;P ) মোড়ানো হয় না। যাইহোক, এটি এই টিউটোরিয়ালে ব্যবহৃত পদ্ধতিগুলিকে কভার করে, এবং আপনি যদি এটিকে প্রসারিত করতে চান তবে আপনাকে সঠিক দিক নির্দেশ করতে হবে। আমি একটি সম্পূর্ণ মোড়ক লেখার পুনর্বিবেচনা করতে পারি, কিন্তু এটি এই টিউটোরিয়ালের সুযোগের বাইরে।

এখন, আমাদের কন্ট্রোলারের মধ্যে, আমরা 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);
  });
};

এবং এর সাথে, আবেদনটি সম্পূর্ণ হওয়া উচিত। node app.js চালিয়ে এটি চেষ্টা করে দেখুন। Socket.IO-কে ধন্যবাদ, অ্যাপ্লিকেশনটি রিয়েল-টাইমে আপডেট করা উচিত।

উপসংহার

এই ইনস্ট্যান্ট মেসেজিং অ্যাপে আপনি আরও অনেক কিছু যোগ করতে পারেন। উদাহরণস্বরূপ, আপনি খালি বার্তা জমা দিতে পারেন। আপনি ক্লায়েন্ট সাইডে এটি প্রতিরোধ করতে ng-valid ব্যবহার করতে পারেন এবং সার্ভারে একটি চেক করতে পারেন। অ্যাপটিতে যোগদানকারী নতুন ব্যবহারকারীদের সুবিধার জন্য সার্ভারটি বার্তার সাম্প্রতিক ইতিহাস রাখতে পারে।

AngularJS অ্যাপগুলি লেখা যেগুলি অন্যান্য লাইব্রেরিগুলি ব্যবহার করে তা সহজ হয়ে যায় একবার আপনি বুঝতে পারলে কীভাবে সেগুলিকে একটি পরিষেবাতে মোড়ানো যায় এবং অ্যাঙ্গুলারকে জানানো হয় যে একটি মডেল পরিবর্তিত হয়েছে৷ পরবর্তীতে আমি AngularJS ব্যবহার করে D3.js , জনপ্রিয় ভিজ্যুয়ালাইজেশন লাইব্রেরি ব্যবহার করার পরিকল্পনা করছি।

তথ্যসূত্র

Angular Socket.IO Seed Finished Instant Messaging App AngularJS Express Socket.IO `