最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

AngularJS+Node.js實現(xiàn)在線聊天室

 更新時間:2015年08月28日 10:55:04   投稿:hebedich  
隨著互聯(lián)網和信息技術的發(fā)展,如何快速構建高效、強大的動態(tài)網站成為很多人研究的熱點。該文將結合AngularJS和Node.js構建一個在線聊天室,體現(xiàn)AngularJs和Node.js整合的優(yōu)點。

不得不說,上手AngularJS比我想象得難多了,把官網提供的PhoneCat例子看完,又跑到慕課網把大漠窮秋的AngularJS實戰(zhàn)系列看了一遍,對于基本的使用依然有很多說不清道不明的疑惑,于是決定通過做一個在線聊天室?guī)椭斫狻EMO可以戳→chat room,代碼可以戳→ChatRoom-AngularJS

清晰圖可以戳 //img.jbzj.com/file_images/article/201508/201508281040051.gif

功能

著手開發(fā)之前,首先明確一下需要實現(xiàn)的功能:

新用戶登入,廣播通知其他用戶
用戶下線,廣播通知其他用戶
可顯示在線人數(shù)及列表
可群聊,可私信
用戶若發(fā)送群消息,廣播通知其他所有用戶
用戶若發(fā)送私信,單獨通知收方界面

因為自己是個審美渣,所以全靠bootstrap了,另外還模仿了下微信聊天記錄里的氣泡設計。

界面分左右兩個板塊,分別用于顯示在線列表和聊天內容。

在左側的在線列表中,點擊不同項可以切換右側板塊的聊天對象。

右側顯示與當前聊天對象的對話記錄,不過僅顯示最近的30條。每一條聊天記錄內容包括發(fā)送人的昵稱及頭像、發(fā)送時間、消息內容。關于頭像,這里做簡單處理,用填充了隨機色的方塊代替。另外,自己發(fā)出去的消息與收到的消息樣式自然要做不同設計,所有效果可以看下圖。

清晰圖可以戳 //img.jbzj.com/file_images/article/201508/201508281040052.png

服務端

服務端我們用Node.js以及混入express、socket.io來開發(fā),在程序根目錄打開終端,執(zhí)行:

復制代碼 代碼如下:
npm init

根據提示,生成一個package.json文件。打開并配置依賴項:

 "dependencies": {
  "express": "^4.13.3",
  "socket.io": "^1.3.6"
 }

之后執(zhí)行 npm install 安裝依賴模塊。

接下來,我們在根目錄下新建app.js,在其中寫Server端代碼。再新建public文件夾,存放client端代碼。

app.js中主要內容如下:

var express = require('express');
var app = require('express')();
var http = require('http').createServer(app);
var io = require('socket.io')(http);

app.use(express.static(__dirname + '/public'));


app.get('/', function (req, res) {
  res.sendfile('index.html');
});


io.on('connection',function(socket){
  socket.on('addUser',function(data){ //有新用戶進入聊天室
  });

  socket.on('addMessage',function(data){ //有用戶發(fā)送新消息
  });
  
  socket.on('disconnect', function () { //有用戶退出聊天室
  );
});

http.listen(3002, function () {
  console.log('listening on *:3002');
});

在上面的代碼中,我們?yōu)橐韵率录砑恿吮O(jiān)聽:

-addUser,有新用戶進入聊天室

該事件由客戶端輸入昵稱后觸發(fā),服務端收到后對昵稱是否已存在進行判斷,如果已存在,通知客戶端昵稱無效:

復制代碼 代碼如下:
socket.emit('userAddingResult',{result:false});

反之,通知客戶端昵稱有效以及當前所有已連接的用戶信息,并把新用戶信息廣播給其他已連接用戶:

socket.emit('userAddingResult',{result:true});
allUsers.push(data);//allUsers保存了所有用戶
socket.emit('allUser',allUsers);//將所有在線用戶發(fā)給新用戶
socket.broadcast.emit('userAdded',data);//廣播歡迎新用戶,除新用戶外都可看到

其中需要注意'socket.emit'與'socket.broadcast.emit'的區(qū)別,可以查看這篇博文socket.io emit的幾種用法解釋

// send to current request socket client
socket.emit('message', "this is a test");
// sending to all clients except sender
socket.broadcast.emit('message', "this is a test");

-addMessage,有用戶發(fā)送新消息

在此事件監(jiān)聽里,需要分成兩類情況處理:

1.私信
如果消息是發(fā)給特定用戶A,那么就需要獲取A對應的socket實例,然后調用其emit方法。所以每當一個客戶端連接到Server端時,我們得把其socket實例保存起來,以備后續(xù)之需。

復制代碼 代碼如下:
connectedSockets[nickname]=socket;//以昵稱作下標,保存每個socket實例,發(fā)私信需要用

需要發(fā)私信時,取出socket實例做操作即可:

復制代碼 代碼如下:
connectedSockets[nickname].emit('messageAdded',data)

2.群發(fā)
群發(fā)就比較簡單了,用broadcast方法即可:

復制代碼 代碼如下:
socket.broadcast.emit('messageAdded',data);//廣播消息,除原發(fā)送者外都可看到

-disconnect,有用戶退出聊天室
需要做三件事情:

1.通知其他用戶“某用戶下線”

復制代碼 代碼如下:
socket.broadcast.emit('userRemoved', data);

2.將用戶從保存了所有用戶的數(shù)組中移除

3.將其socket實例從保存了所有客戶端socket實例的數(shù)組中移除

復制代碼 代碼如下:
delete connectedSockets[nickname]; //刪除對應的socket實例

運行一下服務端代碼,觀察有無錯誤:

復制代碼 代碼如下:
node app.js

若沒什么問題,繼續(xù)編寫客戶端的代碼。

客戶端

在public目錄下新建'index.html',客戶端需要用到bootstrap、angularjs、socket.io、jQuery以及我們自己的js和css文件,先把這些文件用標簽引入。

<!DOCTYPE html>
<html>
<head lang="en">
  <meta charset="UTF-8">
  <title></title>
  <link  rel="stylesheet">
  <link rel="stylesheet" href="./assets/style/app.css"/>
  <script src="http://libs.baidu.com/jquery/2.0.0/jquery.min.js"></script>
  <script src="/socket.io/socket.io.js"></script>
  <script src="http://cdn.bootcss.com/angular.js/1.4.3/angular.min.js"></script>
  <script src="./assets/js/app.js"></script>
</head>
<body></body>
</html>

我們并不立即深入邏輯細節(jié),把框架搭好先。
首先,在body上加上ng-app屬性,標記一下angularjs的“管轄范圍”。這個練習中我們只用到了一個控制器,同樣將ng-controller屬性加到body標簽。

復制代碼 代碼如下:
<body ng-app="chatRoom" ng-controller="chatCtrl">

接下來在js中,我們來創(chuàng)建module及controller。

var app=angular.module("chatRoom",[]);
app.controller("chatCtrl",['$scope','socket','randomColor',function($scope,socket,randomColor){}]);

注意這里,我們用內聯(lián)注入添加了socket和randomColor服務依賴。這里我們不用推斷式注入,以防部署的時候用uglify或其他工具進行了混淆,變量經過了重命名導致注入失效。
在這個練習中,我們自定義了兩個服務,socket和randomColor,前者是對socket.io的包裝,讓其事件進入angular context,后者是個可以生成隨機色的服務,用來給頭像指定顏色。

//socket服務
app.factory('socket', function($rootScope) {
  var socket = io(); //默認連接部署網站的服務器
  return {
    on: function(eventName, callback) {...},
    emit: function(eventName, data, callback) {...}
  };
});

//randomcolor服務
app.factory('randomColor', function($rootScope) {
  return {
    newColor: function() {
      return '#'+('00000'+(Math.random()*0x1000000<<0).toString(16)).slice(-6);//返回一個隨機色
    }
  };
});

注意socket服務中連接的語句“var socket = io();”,我們并沒有傳入任何url,是因為其默認連接部署這個網站的服務器。

考慮到聊天記錄以及在線人員列表都是一個個邏輯及結構重復的條目,且html結構較復雜,為了其復用性,我們把它們封裝成兩個指令:

app.directive('message', ['$timeout',function($timeout) {}])
  .directive('user', ['$timeout',function($timeout) {}]);

注意這里兩個指令都注入了'$timeout'依賴,其作用后文會解釋。

這樣一個外層框架就搭好了,現(xiàn)在我們來完成內部的細節(jié)。

登錄

頁面剛加載時只顯示登錄界面,只有當輸入昵稱提交后且收到服務端通知昵稱有效方可跳轉到聊天室。我們將ng-show指令添加到登錄界面和聊天室各自的dom節(jié)點上,來幫助我們顯示或隱藏元素。用'hasLogined'的值控制是顯示或隱藏。

<!-- chat room -->
<div class="chat-room-wrapper" ng-show="hasLogined">
...
</div>
<!-- end of chat room -->

<!-- login form -->
<div class="userform-wrapper" ng-show="!hasLogined">
...
</div>
<!-- end of login form -->

JS部分

 $scope.login = function() { //登錄
   socket.emit("addUser", {...});
 }

 //收到登錄結果
 socket.on('userAddingResult', function(data) {
   if (data.result) {
     $scope.hasLogined = true;
   } else { //昵稱被占用
     $scope.hasLogined = false;
   }
 });

這里監(jiān)聽了socket連接上的'userAddingResult'事件,接收服務端的通知,確認是否登錄成功。

socket連接監(jiān)聽

成功登錄以后,我們還監(jiān)聽socket連接上的其他事件:

復制代碼 代碼如下:

//接收到歡迎新用戶消息,顯示系統(tǒng)歡迎辭,刷新在線列表
socket.on('userAdded', function(data) {});
//接收到所有用戶信息,初始化在線列表
socket.on('allUser', function(data) {});
//接收到用戶退出消息,刷新在線列表
socket.on('userRemoved', function(data) {});
//接收到新消息,添加到聊天記錄
socket.on('messageAdded', function(data) {});

接收到事件以后,做相應的刷新動作,這里的socket是socket.io經過包裝的服務,內部僅包裝了我們需要用到的兩個函數(shù)on和emit。我們在事件監(jiān)聽里對model做的修改,都會在AngularJS內部得到通知和處理,UI才會得到及時刷新。
監(jiān)聽內做的事情太具體和瑣碎了,這里就不列出了,接下來介紹一下message指令。

message 指令

最后分享一下我在寫message指令時遇到的問題。首先看一下其代碼:

app.directive('message', ['$timeout',function($timeout) {
  return {
    restrict: 'E',
    templateUrl: 'message.html',
    scope:{
      info:"=",
      self:"=",
      scrolltothis:"&"
    },
    link:function(scope, elem, attrs){
        $timeout(scope.scrolltothis);
    }
  };
}])

以及其模板message.html:

<div ng-switch on="info.type">
  <!-- 歡迎消息 -->
  <div class="system-notification" ng-switch-when="welcome">系統(tǒng){{info.text}}來啦,大家不要放過他~</div>
  <!-- 退出消息 -->
  <div class="system-notification" ng-switch-when="bye">系統(tǒng):byebye,{{info.text}}</div>
  <!-- 普通消息 -->
  <div class="normal-message" ng-switch-when="normal" ng-class="{others:self!==info.from,self:self===info.from}">
    <div class="name-wrapper">{{info.from}} @ {{time | date: 'HH:mm:ss' }}</div>
    <div class="content-wrapper">{{info.text}}<span class="avatar"></span></div>
  </div>
</div>

模板中我們用ng-switch指令監(jiān)聽info.type變量的值,根據其值的不同顯示不同內容。比如,當info.type值為"welcome"時,創(chuàng)建第一個dom節(jié)點,刪除下方另外兩個div。
另外,普通消息下,為了在UI上區(qū)分自己發(fā)出去的和收到的消息,需要給他們應用不同的樣式,這里用ng-class指令實現(xiàn)。

復制代碼 代碼如下:
ng-class="{others:self!==info.from,self:self===info.from}"

當'self===info.from'返回true時,應用'self'類,否則,應用'others'類。
在此指令中,我們創(chuàng)建了獨立作用域,并綁定了三個屬性,綁定完后還必須在父作用域的HTML標簽上添加相應屬性。

scope:{
    info:"=",
    self:"=",
    scrolltothis:"&"
}

<message self="nickname" scrolltothis="scrollToBottom()" info="message" ng-repeat="message in messages"></message>

在link函數(shù)中,執(zhí)行一個動作:每當一個message被加到頁面上時,將聊天記錄滾動到最下方,一開始我是這樣寫的:

復制代碼 代碼如下:
link:function(scope, elem, attrs){ scope.scrolltothis();}

結果發(fā)生了一個很奇怪的現(xiàn)象,總是滾動到上一條位置,而不是最新這條。調試之后發(fā)現(xiàn)是因為'scrolltothis'函數(shù)執(zhí)行的時候,DOM還沒渲染,所以在函數(shù)內部獲取scrollHeight的時候獲得的總是添加DOM節(jié)點之前的狀態(tài)。這時候,可以把代碼放到$timeout里延遲0秒執(zhí)行,延遲0秒并不意味著會立即執(zhí)行,因為js的單線程特性,代碼實際會等到dom渲染完再執(zhí)行。

復制代碼 代碼如下:
$timeout(scope.scrolltothis);

完整代碼可以戳我的GitHub→ChatRoom-AngularJS,DEMO可以戳→chat room

有任何不妥之處或錯誤歡迎各位指出,不勝感激~

相關文章

  • 詳解AngularJS中module模塊的導入導出

    詳解AngularJS中module模塊的導入導出

    本文給大家介紹angularjs中module模塊的導入導出,涉及到angularjs module相關知識,對angularjs module感興趣的朋友一起看看吧
    2015-12-12
  • Angular2中select用法之設置默認值與事件詳解

    Angular2中select用法之設置默認值與事件詳解

    在Angular.JS中可以使用數(shù)組或對象創(chuàng)建一個下拉列表選項。關于Angular.js中select的基礎相信大家應該都已經了解了,那么下面這篇文章主要給大家介紹了Angular2中select用法之設置默認值與事件的相關資料,需要的朋友可以參考借鑒,下面來一起看看吧。
    2017-05-05
  • 詳解如何構建一個Angular6的第三方npm包

    詳解如何構建一個Angular6的第三方npm包

    這篇文章主要介紹了詳解如何構建一個Angular6的第三方npm包,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2018-09-09
  • 玩轉Koa之koa-router原理解析

    玩轉Koa之koa-router原理解析

    本文將要分析的是經常用到的路由中間件 -- koa-router,詳細的介紹了koa-router概述和實現(xiàn),小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2018-12-12
  • Angular JS 生成動態(tài)二維碼的方法

    Angular JS 生成動態(tài)二維碼的方法

    這篇文章主要介紹了Angular JS 生成動態(tài)二維碼的方法,非常不錯,具有參考借鑒價值,需要的朋友可以參考下
    2017-02-02
  • angularjs+bootstrap菜單的使用示例代碼

    angularjs+bootstrap菜單的使用示例代碼

    本篇文章主要介紹了angularjs+bootstrap菜單的使用示例代碼,具有一定的參考價值,感興趣的小伙伴們可以參考一下。
    2017-03-03
  • AngularJS 遇到的小坑與技巧小結

    AngularJS 遇到的小坑與技巧小結

    本文主要對AngularJS 遇到的小坑與技巧進行了一個小小的總結,比較使用,希望能給大家做一個參考。
    2016-06-06
  • AngularJS ng-template寄宿方式用法分析

    AngularJS ng-template寄宿方式用法分析

    這篇文章主要介紹了AngularJS ng-template寄宿方式用法,結合實例形式分析了ng-template模板的相關使用技巧,需要的朋友可以參考下
    2016-11-11
  • 詳解AngularJS中的表單驗證(推薦)

    詳解AngularJS中的表單驗證(推薦)

    AngularJS自帶了很多驗證,什么必填,最大長度,最小長度...,這里記錄幾個有用的正則式驗證。本文給大家介紹的非常詳細,對angularjs中表單驗證知識感興趣的朋友一起學習吧
    2016-11-11
  • Web開發(fā)使用Angular實現(xiàn)用戶密碼強度判別的方法

    Web開發(fā)使用Angular實現(xiàn)用戶密碼強度判別的方法

    這篇文章主要介紹了Web開發(fā)使用Angular實現(xiàn)用戶密碼強度判別的方法,需要的朋友可以參考下
    2017-09-09

最新評論

揭东县| 郑州市| 忻城县| 乐亭县| 博爱县| 高雄县| 泸州市| 连平县| 宣化县| 安新县| 顺昌县| 南川市| 柳河县| 开远市| 伊春市| 登封市| 台江县| 广灵县| 天镇县| 肇东市| 舟曲县| 全南县| 富平县| 商南县| 上蔡县| 齐齐哈尔市| 万载县| 阳谷县| 新安县| 昌邑市| 广宗县| 抚松县| 河津市| 平定县| 家居| 宿州市| 淮阳县| 普定县| 河南省| 武穴市| 松原市|