我不得不说,开始使用AngularJS比我想象的要困难得多。看了官网提供的PhoneCat示例后,我去了海量开放在线课程。com并观看了《沙漠中的穷秋》中的AngularJS实战系列。对于基本的使用还是有很多疑惑的,所以我决定通过做一个在线聊天室来帮助我理解。DEMO可以盖章聊天室,代码可以盖章聊天室-AngularJS。
清晰的图片可以盖章//files . JB 51 . net/file _ images/article/201508281040051 . gif。
功能
在开始开发之前,首先明确要实现的功能:
新用户登录,广播通知其他用户注销,广播通知其他用户显示在线号码和列表,分组聊天。如果用户发送群组消息,广播通知所有其他用户。如果用户发送私人消息,单独通知接收方接口。
因为我是审美人渣,所以要靠自举,微信聊天记录里也模仿了泡泡设计。
界面分为左右两部分,分别用于显示在线列表和聊天内容。
在左侧的在线列表中,单击不同的项目来切换右侧的聊天对象。
右侧显示与当前聊天对象的对话记录,但只显示最新的30条。每个聊天记录包括发件人的昵称和头像、发送时间和消息内容。关于头像,在这里做简单的处理,用随机颜色填充的方块代替。另外,自己发的消息和收到的消息风格自然是设计不同的,所有的效果都可以在下图中看到。
清晰的图片可以盖章//files . JB 51 . net/file _ images/article/201508281040052 . png。
计算机网络服务器
我们使用Node.js和express以及socket.io开发服务器,在程序根目录下打开终端,执行:
复制代码如下:npm init
根据提示,生成一个package.json文件。打开并配置依赖关系:
执行npm install,在“dependencies”: {“express”:“4 . 13 . 3”、“socket . io”:“1 . 3 . 6”}之后安装相关模块。
接下来,我们在根目录下创建一个新的app.js,并在其中编写服务器端代码。然后创建一个新的公共文件夹来存储客户端代码。
app.js中的主要内容如下:
var express=require(' express ');var app=require(' express ')();var http=require('http ')。createServer(应用程序);var io=require(' socket . io ')(http);app . use(express . static(_ _ dirname '/public ');app.get('/'),function (req,RES){ RES . send file(' index . html ');});Io.on ('connection ',function(socket){ socket . on(' adduser ',function(data){//新用户进入聊天室});Socket.on ('addmessage ',函数(数据){//用户发送新消息});插座。on ('disconnect ',function(){//某用户已退出聊天室);});http.listen(3002,function(){ console . log(' listening on * :3002 ');});在上面的代码中,我们添加了对以下事件的监听:
-添加用户,新用户进入聊天室
此事件在客户端输入昵称后触发。服务器收到后,判断昵称是否已经存在,如果存在,通知客户端昵称无效:
复制代码如下: socket . emit(' user addingresult ',{ result : false });
相反,通知客户端昵称有效以及所有当前连接用户的信息,并将新用户信息广播给其他连接用户:
socket . emit(' useredingresult ',{ result : true });allUsers.push(数据);//allUsers保存所有用户socket.emit('allUser ',all users//将所有在线用户发送到新用户socket.broadcast.emit('用户已添加',数据);//广播欢迎新用户。除了新用户,你可以看到“socket.emit”和“socket.broadcast.emit”的区别。你可以查看这篇博文socket.io emit的几种用法解释:
//发送到当前请求套接字clientsocket.emit('message ','这是一个测试');//发送给除sendersocket.broadcast.emit以外的所有客户端(' message ','这是一个测试');-添加消息,用户发送了新消息
在这种事件监控中,需要将其分为两种情况:
1.私有消息如果消息被发送给特定用户a,则需要获取对应于a的套接字实例,然后调用其emit方法。因此,每当客户端连接到服务器时,我们必须保存其套接字实例以备将来需要。
复制代码如下:连接套接字[昵称]=socket;//使用昵称作为下标保存每个套接字实例,这是发送私有消息所需要的
当需要发送私有消息时,取出套接字实例并执行以下操作:
复制代码如下:连接的套接字[昵称]。发出(已添加消息,数据)
2.群发比较简单,就用广播的方法:
复制代码如下:socket.broadcast.emit('添加消息',数据);//除了原发件人,可以看到广播消息
-断开连接,用户需要做三件事才能退出聊天室:
1.通知其他用户“用户已离线”
复制代码如下:socket.broadcast.emit('用户已删除,数据);
2.从保存所有用户的阵列中删除用户
3.从保存所有客户端套接字实例的数组中移除其套接字实例
复制代码如下:删除连接的套接字[昵称];//删除相应的套接字实例
运行服务器代码并观察是否有任何错误:
复制代码如下: nodeapp.js。
如果没有问题,继续编写客户端代码。
客户
要在公共目录中创建‘index . html’,客户端需要使用bootstrap、angularjs、socket.io、jQuery以及我们自己的js和css文件,首先用标签引入这些文件。
!DOCTYPE html html head lang=' en ' meta charset=' UTF-8 ' title/title link href=' http://cdn . bootscs.com/bootstrap/3 . 3 . 5/CSS/bootstrap . min . CSS ' rel='样式表' link rel='样式表' href='。/assets/style/app . CSS '/script src=' http :http://libs . Baidu.com/jquery/2 . 0 . 0/jquery . min . js '/script script src=' http :/socket . io/socket . io . js '/script script src=' http://cdn . bootscs.com/angular . js/1 . 4 . 3/angular . min . js '/script script src=' http 33333/assets/js/app . js '/script/head body/body/html我们不马上深入逻辑细节,先设置框架。首先,在主体中添加ng-app属性来标记angularjs的“管辖范围”。在本练习中,我们只使用了一个控制器,并且还在body标签中添加了ng-controller属性。
复制代码如下: body ng-app=' chat room ' ng-controller=' chat ctrl '
接下来,在js中,我们将创建模块和控制器。
var app=angular . module(' ChatRoom ',[]);app.controller('chatCtrl ',['$scope ',' socket ',' randomColor ',function($scope,socket,randomColor){ }]);请注意,我们已经通过内联注入添加了套接字和randomColor服务依赖项。这里我们不使用推断注入,以防部署时与uglify或者其他工具混淆,变量被重命名,导致注入无效。在本练习中,我们定制了两个服务,socket和randomColor。前者是socket.io的一个包,让它的事件进入角度上下文,而后者是一个服务,可以生成随机的颜色来给头像分配颜色。
//socket service app . factory(' socket ',function($ root scope){ var socket=io();//连接到服务器返回{ :函数(事件名称,回调)}.},emit:函数(事件名称、数据、回调){ 0.}}的部署网站;});//random color service app . factory(' random color ',function($ root scope){ return { new color : function(){ return ' # '(' 00000 '(math . random()*0x 1000000))。tostring (16))。slice//返回随机颜色} };});注意语句“var socket=io();”我们没有传入任何url,因为它连接到默认情况下部署此网站的服务器。
考虑到聊天记录和在线人员列表是具有重复逻辑和结构的项目,并且html结构复杂,为了它们的可重用性,我们将它们封装成两条指令:
app.directive('message ',['$timeout ',function($timeout) {}])。指令(' user ',['$timeout ',函数($ time out){ }]);请注意,这里的两条指令都注入了“$timeout”依赖项,其功能将在后面解释。
这样一个外框就搭建好了,现在我们来完成内部细节。
登录
页面刚加载时,只显示登录界面,只有提交输入昵称,服务器通知昵称有效,才能跳转到聊天室。我们在登录界面和聊天室的dom节点中添加ng-show指令,帮助我们显示或隐藏元素。使用“hasLogined”的值来控制它是显示还是隐藏。
!-聊天室- div class='聊天室-包装' ng-show='hasLogined './div!-聊天室结束-!-登录表单- div类='userform-wrapper' ng-show='!' hasLogined './div!-登录表单结束- JS部分
$ scope . log in=function(){//log in socket . emit(' AddUser ',{ 0.});}//收到登录结果socket.on ('useraddingresult ',function(data){ if(data . result){ $ scope . has log=true;} else {//昵称占用了$ scope。haslogged=false} });在这里,我们监听了套接字连接上的‘useredingresult’事件,收到了来自服务器的通知,并确认登录是否成功。
套接字连接监控
成功登录后,我们还会监控套接字连接上的其他事件:
复制代码如下://收到新用户欢迎消息后,显示系统欢迎辞,刷新在线列表socket.on('用户已添加',函数(数据){ });//收到所有用户信息后,初始化online list socket.on ('alluser ',function(data){ });//收到用户退出消息后,刷新online list socket.on('用户已删除',函数(数据){ });//接收新消息并将其添加到聊天记录socket.on('消息已添加',函数(数据){ });
收到事件后,做相应的刷新动作。这里的socket是socket.io的打包服务,里面只打包了我们需要用到的on和emit两个函数。我们在事件监控中对模型所做的修改会在AngularJS中得到通知和处理,UI也会及时刷新。监控中做的事情太具体太琐碎,这里就不一一列举了。接下来,我们将介绍消息命令。
消息指令
最后,我想分享一下我在编写消息命令时遇到的问题。首先看看代码:
app.directive('message ',['$timeout ',function($ time out){ return { restrict : ' E ',templateUrl: 'message.html ',scope:{ info:'=',self:'=',scrolltothis: ' ' },link:function(scope,elem,attrs){ $ time out(scope . scroltothis);} };}])及其模板message.html:
div ng-打开='info.type '!-欢迎消息-div class=' system-notification ' ng-switch-当=' welcome' system {{info.text}}来了,我们不要让他走~/div!-退出消息-div class=' system-notification ' ng-switch-when=' bye ' system : bye,{{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 3360s ' } }/Div class=' content-wrapper ' { info . text } } span class=' avatar '/span/Div/Div/Div模板中,我们使用ng-switch指令监听info.type变量的值,根据不同的值显示不同的内容。例如,当info.type值为“欢迎”时,创建第一个dom节点并删除下面的另外两个div。另外,在普通消息下,为了区分UI上发送和接收的消息,需要对它们应用不同的样式,这里通过ng-class指令来实现。
复制代码如下: ng-class=' { others 3360 self!==info.from,self:self===info.from} '
当“self===info.from”返回true时,将应用“self”类;否则,将应用“其他”类。在这个指令中,我们创建了一个独立的作用域,并绑定了三个属性。绑定后,我们必须给父范围的HTML标记添加相应的属性。
scope:{ info:'=',self:'=',Scroll to this : ' ' } message self='昵称' Scroll to bottom()' info=' message ' ng-repeat=' messages in messages '/message在链接功能中,执行一个操作:每当有消息添加到页面中时,将聊天记录滚动到底部,这是我最初写的。
复制代码如下:Link 3360Function (scope,elem,attrs) {scope。滚动到此();}
结果,一个奇怪的现象发生了,总是滚动到前一个位置,而不是最新的位置。调试后发现,在执行‘scroltothis’函数时,DOM还没有被渲染,所以在函数内部获取scrollHeight时,总是获取添加DOM节点前的状态。此时,您可以将代码放入$timeout中,并将执行延迟0秒。延迟0秒并不意味着立即执行。由于js的单线程特性,代码实际上会一直等到dom被呈现。
复制代码如下: $ timeout(作用域。滚动到此);
完整代码可以戳我的GitHub聊天室-AngularJS,DEMO可以戳聊天室
请指出任何不足或错误,非常感谢~