带有Socket.io的AngularJS协作板

作者: Peter Berry
创建日期: 14 七月 2021
更新日期: 13 可能 2024
Anonim
Take a sneak peek at YouTube Live Streaming APIs - Jarek Wilkiewicz
视频: Take a sneak peek at YouTube Live Streaming APIs - Jarek Wilkiewicz

内容

  • 所需知识: 中级JavaScript
  • 要求: Node.js,NPM
  • 项目时间: 2小时

AngularJS特别适合在浏览器中创建丰富的客户端应用程序,并且当您在混合中添加一些Socket.io时,事情会变得非常有趣。在本文中,我们将构建一个实时协作板,该板将AngularJS用于客户端应用程序,并使用Socket.io在所有连接的客户端之间共享状态。

在开始之前,让我们先介绍一下客房整理。我假设您对HTML和JavaScript有基本的了解,因为我不会覆盖代码的每个小角落。例如,由于其中没有新信息,因此我不会调出包含在HTML文件开头的CSS和JavaScript文件。

此外,我鼓励您从我的GitHub帐户中获取代码以继续学习。我的好朋友布莱恩·福特(Brian Ford)也拥有出色的Socket.io种子,我将其最初的想法作为基础。

我们希望在协作板上拥有四个主要功能,即能够创建便笺,阅读便笺,更新便笺,删除便笺,以及为了有趣而在便笺簿上移动便笺的功能。是的,没错,我们专注于标准CRUD功能。我相信,通过专注于这些基本功能,我们将涵盖足够的代码以使模式出现,以便您可以将其应用到其他地方。


01.服务器

我们将首先从Node.js服务器开始,因为它将作为我们构建其他所有基础的基础。

我们将使用Express和Socket.io构建Node.js服务器。我们使用Express的原因是,它提供了一种在Node.js中设置静态资产服务器的良好机制。 Express附带了很多很棒的功能,但是在这种情况下,我们将使用它在服务器和客户端之间将应用程序一分为二。

(我假设您已经安装了Node.js和NPM。如果没有安装Google,快速的Google搜索将向您显示如何安装它们。)

02.裸露的骨头

因此,要构建服务器的基础,我们需要做一些事情来启动和运行。

// app.js

// A.1
var express = require('express'),
app = express();
server = require(‘http’)。createServer(app),
io = require(’socket.io’)。listen(服务器);

// A2
app.configure(function(){
app.use(express.static(__ dirname +‘/ public’));
});

// A.3
server.listen(1337);


A.1我们正在声明和实例化我们的Node.js模块,以便我们可以在我们的应用程序中使用它们。我们声明了Express,实例化Express,然后创建一个HTTP服务器并将Express实例发送到其中。然后从那里实例化Socket.io,并告诉它监视我们的服务器实例。

A.2然后,我们告诉Express应用使用公共目录来提供文件。

A.3我们启动服务器,并告诉它监听端口 1337.

到目前为止,这已经非常轻松快捷。我相信我们的代码不足10行,并且已经有了功能正常的Node.js服务器。向前!

03.声明您的依赖项

//packages.json
{
“ name”:“ angular-collab-board”,
“ description”:“ AngularJS协作委员会”,
“ version”:“ 0.0.1-1”,
“私人”:是的,
“依赖关系”:{
“ express”:“ 3.x”,
“ socket.io”:“ 0.9.x”
}
}

NPM最好的功能之一就是能够在 packages.json 文件,然后通过自动安装 npm安装 在命令行上。


04.连接Socket.io

我们已经定义了应用程序所需的核心功能,因此我们需要设置Socket.io事件侦听器和适当的闭包以处理每个操作的事件。

在下面的代码中,您会注意到它实质上是事件监听器和回调的配置。第一个事件是 联系 事件,我们用它来连接闭包中的其他事件。

io.sockets.on(“连接”,功能(插座){
socket.on('createNote',function(data){
socket.broadcast.emit(“ onNoteCreated”,数据);
});

socket.on('updateNote',function(data){
socket.broadcast.emit(“ onNoteUpdated”,数据);
});

socket.on('deleteNote',function(data){
socket.broadcast.emit(“ onNoteDeleted”,数据);
});

socket.on('moveNote',function(data){
socket.broadcast.emit(“ onNoteMoved”,数据);
});
});

在这里,我们将侦听器添加到 createNote, 更新说明, deleteNotemoveNote。在回调函数中,我们只是广播发生了什么事件,以便任何监听的客户端都可以得到通知。

关于个别事件处理程序中的回调函数,有几点需要指出。第一,如果您想将事件发送给其他人,但发出事件的客户端除外,请插入 播送 之前 发射 函数调用。其次,我们只是将事件的有效负载传递给感兴趣的各方,以便他们可以按照自己认为合适的方式进行处理。

05.启动引擎!

现在,我们已经定义了依赖项,并使用Express和Socket.io功能设置了Node.js应用程序,初始化Node.js服务器非常简单。

首先,您像这样安装Node.js依赖项:

npm安装

然后像这样启动服务器:

节点app.js

然后!您在浏览器中转到该地址。 am!

06.继续前进之前的一些坦率想法

我主要是一名前端开发人员,最初对将Node.js服务器连接到我的应用程序感到有些害怕。 AngularJS部分很简单,但是服务器端JavaScript?将恐怖片中令人毛骨悚然的音乐排入队列。

但是,我绝对地发现我可以仅用几行代码就可以建立一个静态Web服务器,然后再使用Socket.io来处理浏览器之间的所有事件,则需要几行。而且仍然只是JavaScript!为了及时起见,我们仅介绍了一些功能,但我希望到本文结尾时您会发现它容易游泳-游泳池的深处并不那么令人恐惧。

07.客户

现在我们已经为服务器奠定了坚实的基础,让我们继续介绍我最喜欢的部分-客户!我们将使用AngularJS,jQueryUI作为可拖动部分以及Twitter Bootstrap作为样式库。

08.裸露的骨头

根据个人喜好,当我启动一个新的AngularJS应用程序时,我想快速定义我所知道的最低要求,然后尽快开始迭代。

每个AngularJS应用程序都需要使用至少一个存在的控制器进行引导,因此通常这是我总是开始的地方。

要自动引导应用程序,您只需添加 ng-app 到您希望应用程序所在的HTML节点。在大多数情况下,将其添加到HTML标记中将是完全可以接受的。我还添加了一个属性 ng-app 告诉我我想使用 应用程序 模块,我将在稍后定义。

// public / index.html
html ng-app =“ app”>

我知道我至少需要一个控制器,因此我将使用 ng控制器 并为其分配一个属性 MainCtrl.

body ng-controller =“ MainCtrl”> / body>

因此,现在我们可以使用名为 应用程序 和一个名为 MainCtrl。让我们继续并立即创建它们。

创建模块非常简单。您可以通过调用来定义它 角度模块 并给它起个名字。为了将来参考,空数组的第二个参数是您可以在其中注入子模块以在应用程序中使用的参数。它不在本教程的讨论范围之内,但是当您的应用程序的复杂性和需求开始增长时,它很方便。

// public / js / collab.js
var app = angular.module(‘app’,[]);

我们将在中声明一些空的占位符 应用程序 以开头的模块 MainCtrl 以下。我们将在稍后填充所有内容,但我想从一开始就说明其基本结构。

app.controller('MainCtrl',function($ scope){});

我们还将将Socket.io功能包装在一个 插座 服务,这样我们就可以封装该对象,而不会使其漂浮在全局名称空间中。

app.factory('socket',function($ rootScope){});

当我们这样做时,我们将声明一个名为 便签 我们将用来封装便签功能的地方。

app.directive('stickyNote',function(socket){});

因此,让我们回顾一下我们到目前为止所做的事情。我们使用以下命令引导了应用程序 ng-app 并在HTML中声明了我们的应用程序控制器。我们还定义了应用程序模块并创建了 MainCtrl 控制器, 插座 服务与 便签 指示。

09.创建便签

现在我们已经有了AngularJS应用程序的框架,我们将开始构建创建功能。

app.controller('MainCtrl',function($ scope,socket){// B.1
$ scope.notes = []; // B.2

//传入
socket.on('onNoteCreated',function(data){// B.3
$ scope.notes.push(data);
});

//传出
$ scope.createNote = function(){// B.4
var note = {
id:new Date()。getTime(),
标题:“新笔记”,
正文:“待处理”
};

$ scope.notes.push(note);
socket.emit('createNote',note);
};

B.1 AngularJS内置了依赖项注入功能,因此我们正在注入 $范围 对象和 插座 服务。这 $范围 对象用作ViewModel,基本上是一个JavaScript对象,其中嵌入了一些事件以启用双向数据绑定。

B.2我们声明了用于绑定视图的数组。

B.3我们正在为 onNoteCreated 上的事件 插座 服务并将事件有效负载推入 $ scope.notes 大批。

B.4我们宣布了 createNote 创建默认值的方法 笔记 对象并将其推入 $ scope.notes 大批。它还使用 插座 服务发出 createNote 事件并通过 新笔记 沿着对象。

现在我们有了创建便笺的方法,我们如何称呼它呢?这是一个好问题!在HTML文件中,我们添加了内置的AngularJS指令 ng-点击 到按钮,然后添加 createNote 方法调用作为属性值。

button id =“ createButton” ng-click =“ createNote()”>创建记事/按钮>

是时候快速回顾一下我们到目前为止所做的事情了。我们在其中添加了一个数组 $范围 中的对象 MainCtrl 这将保存该应用程序的所有注释。我们还添加了一个 createNote 上的方法 $范围 对象以创建新的本地注释,然后通过 插座 服务。我们还在上添加了一个事件监听器 插座 服务,这样我们就可以知道其他客户何时创建了便笺,因此可以将其添加到我们的收藏中。

10.显示便签

现在,我们可以创建注释对象并在浏览器之间共享它,但实际上如何显示它呢?这是指令出现的地方。

指令及其复杂性是一个广泛的主题,但简短的版本是它们提供了一种使用自定义功能扩展元素和属性的方法。指令很容易成为我最喜欢AngularJS的部分,因为它实际上使您可以用HTML围绕应用程序创建整个DSL(特定于域的语言)。

很自然,由于我们将为协作委员会创建便签,因此我们应该创建一个便笺 便签 指示。通过在要声明其的模块上调用指令方法并传入返回指令定义对象的名称和函数来定义指令。指令定义对象具有许多可以在其上定义的属性,但是在这里,我们仅将其中一些用于我们的目的。

我建议您查看AngularJS文档,以查看可以在指令定义对象上定义的属性的完整列表。

app.directive('stickyNote',function(socket){
var linker = function(scope,element,attrs){};

var controller = function($ scope){};

返回 {
限制:‘A’,// C.1
链接:链接器,// C.2
控制器:控制器,// C.3
范围:{// C.4
注意:‘=’,
删除:‘&’
}
};
});

C.1您可以将指令限制为某种类型的HTML元素。最常见的两个是元素或属性,您可以使用进行声明 E 一种 分别。您也可以将其限制为CSS类或注释,但并不常见。

C.2 link函数是放置所有DOM操作代码的地方。我发现了一些例外,但这始终是正确的(至少有99%的时间)。这是AngularJS的基本原则,也是我强调它的原因。

C.3控制器功能的工作方式与我们为应用程序定义的主控制器相同,但 $范围 我们传递的对象特定于指令所基于的DOM元素。

C.4 AngularJS具有隔离范围的概念,它使您可以显式定义指令的范围与外界的通信方式。如果未声明作用域,则该指令将隐式地从具有父子关系的父作用域继承。在很多情况下,这不是最佳选择。通过隔离范围,我们减少了外界可能无意中和不利地影响您的指令状态的机会。

我已经声明了双向数据绑定 笔记= 符号和绑定到的表达式 删除& 象征。请阅读AngularJS文档以获取隔离范围的完整说明,因为隔离范围是框架中较复杂的主题之一。

因此,让我们实际在DOM中添加便签。

像其他任何好的框架一样,AngularJS带有一些非常出色的功能,它们是开箱即用的。最方便的功能之一是 ng-repeat。此AngularJS指令允许您传入对象数组,并且它重复存在的任何标签的次数与数组中项目的次数相同。在以下情况下,我们正在迭代 笔记 数组并复制 div 元素及其子元素的长度 笔记 大批。

div sticky-note ng-repeat =“注释中的注释” note =“ note” ondelete =“ deleteNote(id)”>
按钮type =“ button” ng-click =“ deleteNote(note.id)”>×/ button>
输入ng-model =“ note.title” ng-change =“ updateNote(note)” type =“ text”>
textarea ng-model =“ note.body” ng-change =“ updateNote(note)”
> {{note.body}} / textarea>
/ div>

的美丽 ng-repeat 它绑定到您传入的任何数组,并且当您向数组添加项目时,您的DOM元素将自动更新。您可以更进一步,不仅重复标准DOM元素,还重复其他自定义指令。这就是为什么你看到 便签 作为元素上的属性。

自定义代码还有另外两点需要澄清。我们已经隔离了范围 便利贴 关于两个属性的指令。第一个是绑定定义的隔离作用域 笔记 财产。这意味着,只要注释对象在父范围中发生更改,它将自动更新指令中的相应注释对象,反之亦然。另一个定义的隔离范围位于 删除 属性。这意味着什么时候 删除 在指令中被调用,它将调用 删除 实例化指令的DOM元素上的属性。

实例化指令后,指令将被添加到DOM中,并调用link函数。这是在元素上设置一些默认DOM属性的绝佳机会。我们传入的element参数实际上是一个jQuery对象,因此我们可以对其执行jQuery操作。

(实际上,AngularJS内置了一部分jQuery,但是如果您已经包含完整版的jQu​​ery,则AngularJS会遵循它。)

app.directive('stickyNote',function(socket){
var linker = function(scope,element,attrs){
//一些DOM初始化使其变得更好
element.css('left','10px');
element.css('top','50px');
element.hide()。fadeIn();
};
});

在上面的代码中,我们只是将便签放置在舞台上并使其淡入。

11,删除便签

现在我们可以添加和显示便签了,现在该删除便签了。便笺的创建和删除是从便笺绑定到的数组中添加和删除项目的问题。这是父作用域维护该数组的责任,这就是为什么我们从指令中发出删除请求,但让父作用域进行实际的繁重工作的原因。

这就是为什么我们要在指令上创建表达式定义的隔离范围的所有麻烦:因此指令可以在内部接收delete事件并将其传递给其父级进行处理。

注意指令中的HTML。

按钮type =“ button” ng-click =“ deleteNote(note.id)”>×/ button>

我要说的下一件事情似乎还有很长的路要走,但请记住我们站在同一边,经过我的阐述,这将是有道理的。单击粘滞便笺右上角的按钮时,我们正在呼叫 deleteNote 在指令的控制器上并传递 note.id 价值。控制器然后调用 删除 然后执行我们连接到它的任何表达式。到现在为止还挺好?我们正在控制器上调用一个本地方法,然后通过调用隔离范围中定义的任何表达式将其传递给控制器​​。在父级上被调用的表达式恰好被调用了 deleteNote 也一样

app.directive('stickyNote',function(socket){
var controller = function($ scope){
$ scope.deleteNote = function(id){
$ scope.ondelete({
ID:ID
});
};
};

返回 {
限制:“ A”,
链接:链接器,
控制器:控制器,
范围: {
注意:‘=’,
删除:‘&’
}
};
});

(使用表达式定义的隔离范围时,参数在对象映射中发送。)

在父范围内 deleteNote 被调用,并使用 每个角度 实用程序函数,以遍历notes数组。该职能部门处理完本地业务后,它将继续进行并发出事件,以供世界其他地区做出相应反应。

app.controller('MainCtrl',function($ scope,socket){
$ scope.notes = [];

//传入
socket.on('onNoteDeleted',function(data){
$ scope.deleteNote(data.id);
});

//传出
$ scope.deleteNote = function(id){
var oldNotes = $ scope.notes,
newNotes = [];

angular.forEach(oldNotes,function(note){
if(note.id!== id)newNotes.push(note);
});

$ scope.notes = newNotes;
socket.emit('deleteNote',{id:id});
};
});

12.更新便签

我们正在取得惊人的进步!到现在为止,我希望您开始看到我们正在进行的旋风之旅中出现的一些模式。列表中的下一项是更新功能。

我们将从实际的DOM元素开始,并一直跟踪到服务器,再到客户端。首先,我们需要知道便笺的标题或正文何时更改。 AngularJS将表单元素视为数据模型的一部分,因此您可以快速关联双向数据绑定。为此,请使用 ng模型 指令,然后放入要绑定的属性。在这种情况下,我们将使用 note.title笔记主体 分别。

当这些属性中的任何一个发生更改时,我们都希望捕获该信息以传递。我们通过 ng-change 指令并使用它来调用 更新说明 并传递便笺对象本身。 AngularJS进行了一些非常聪明的脏检查,以检测是否存在任何值 ng模型 已经更改,然后执行其中的表达式 ng-change.

输入ng-model =“ note.title” ng-change =“ updateNote(note)” type =“ text”>
textarea ng-model =“ note.body” ng-change =“ updateNote(note)”> {{note.body}} / textarea>

使用的好处 ng-change 是当地的变革已经发生,我们只是负责传达信息。在控制器中 更新说明 被称为,从那里我们将发出 更新说明 我们的服务器向其他客户端广播的事件。

app.directive('stickyNote',function(socket){
var controller = function($ scope){
$ scope.updateNote = function(note){
socket.emit('updateNote',note);
};
};
});

在指令控制器中,我们正在监听 onNoteUpdated 知道另一个客户的便笺何时更新的事件,以便我们可以更新本地版本。

var controller = function($ scope){
//传入
socket.on('onNoteUpdated',function(data){
//更新相同的音符
if(data.id == $ scope.note.id){

$ scope.note.title = data.title;
$ scope.note.body = data.body;
}
});
};

13.移动便签

至此,我们基本上已经在CRUD的小朋友泳池周围跑了一圈,生活很美好!为了使您的朋友印象深刻,我们将增加在屏幕上移动笔记并实时更新坐标的功能。别着急-这只是几行代码。所有这些艰苦的工作将获得回报。我保证!

我们已经邀请了特别嘉宾jQueryUI参加派对,而所有这些事情都是为可拖动对象完成的。添加在本地拖动笔记的功能仅需一行代码。如果添加 element.draggable(); 进入链接器功能后,您将开始听到Survivor的“老虎之眼”,因为您现在可以拖动笔记了。

我们想知道何时拖动停止并捕获要传递的新坐标。 jQueryUI是由一些非常聪明的人构建的,因此当拖动停止时,您只需要为stop事件定义一个回调函数即可。我们抓住 note.id 离开范围对象以及左侧和顶部的CSS值 ui 目的。有了这些知识,我们就可以做我们一直以来所做的事情:发出!

app.directive('stickyNote',function(socket){
var linker = function(scope,element,attrs){
element.draggable({
停止:function(event,ui){
socket.emit('moveNote',{
id:scope.note.id,
x:ui.position.left,
y:ui.position.top
});
}
});

socket.on('onNoteMoved',function(data){
//更新相同的音符
if(data.id == scope.note.id){
element.animate({
左:data.x,
顶部:data.y
});
}
});
};
});

在这一点上,我们也正在监听套接字服务中与移动相关的事件,这不足为奇。在这种情况下 onNoteMoved 事件,如果注释匹配,那么我们将更新左侧和顶部的CSS属性。 am!完毕!

14.奖金

如果我不能完全确定您可以在不到10分钟的时间内实现这一目标,那么我将不包括这部分奖金。我们将部署到实时服务器上(我仍然对它的简单性感到惊讶)。

首先,您需要注册免费的Nodejitsu试用版。试用版免费提供30天,这非常适合您弄湿自己的脚。

创建帐户后,您需要安装jitsu软件包,您可以从命令行通过以下方式进行安装: $ npm安装jitsu -g.

然后您需要通过以下命令从命令行登录 $ jitsu登录 并输入您的凭据。

确保您直接在应用程序中,输入 $柔术部署 并逐步解决问题。我通常会尽量保留默认值,这意味着我给我的应用程序起了一个名字,但没有一个子域等。

而且,我亲爱的朋友们,仅此而已!部署并准备就绪后,您将从服务器的输出中获取应用程序的URL。

15.结论

我们在本文中介绍了许多AngularJS,希望您在此过程中获得很多乐趣。我认为用AngularJS和Socket.io可以在大约200行代码中完成的工作真的很整洁。

为了专注于要点,我没有涉及几件事,但是我鼓励您放下源码并试用该应用程序。我们已经建立了坚实的基础,但是您仍然可以添加许多功能。入侵!

Lukas Ruebbelke是一位技术狂热者,并与人合着了《 AngularJS in Manning Publications在行动》。他最喜欢做的事情是使人们对他的新技术同样感到兴奋。他管理着Phoenix Web应用程序用户组,并与其他犯罪同伴一起主持了多次黑客马拉松。

喜欢这个吗?阅读这些!

  • 如何制作应用
  • 我们最喜欢的网络字体-而且一分钱都不花钱
  • 探索增强现实的下一步
  • 下载免费纹理:高分辨率,可以立即使用
最新帖子
Non-Format如何设计CAC品牌宣传年度版式
更远

Non-Format如何设计CAC品牌宣传年度版式

富有表现力的排版,出色的插图和醒目的图形风格无国界–听起来熟悉吗? Non-Format是全球最令人兴奋的设计工作室之一,以其创新的创作方向和引人注目的作品而著称。自从英国设计师Jon For 和挪威人Kjell Ekhorn于2000年合作以来,两人一直为客户提供具有开创性的设计和品牌,从可口可乐等商业巨头到独立唱片公司,出版社等等。今年早些时候,For 和Ekhorn为《计算机艺术精选:201...
开始使用Rust
更远

开始使用Rust

C编程语言确实通过了时间的考验。在极少数情况下,它不会蓬勃发展。这主要是由于它的高执行性能,不幸的是,这要付出一定的代价:C不支持现代编程语言中预期的许多功能。Mozilla揭示了全新的品牌形象Mozilla Re earch的Ru t是一种尝试创建更好的捕鼠器的尝试。它的语言设计始终侧重于高性能,并且接近于硬件。但是,其语法和编译器也利用了现代编程语言研究提供的各种好处。鉴于Mozilla的使命...
10个惊人的美丽冬季设计
更远

10个惊人的美丽冬季设计

炎热的冬天已经到了,你们中的许多人都裹足不前,安顿下来,度过了严重的冬眠。在假期之前,您需要尽心尽力地准备圣诞节的最后一刻,而我们却收集了受寒冷冬日启发而来的最佳设计。请务必仔细阅读本包装!冬季最令人难过的方面之一是,秋天期间装饰树木的所有美丽的红色,黄色,橙色和棕色叶子掉落在地板上-在其后留下了鲜明而赤裸的树枝。值得庆幸的是,由Twentyfir t设计的这个书架已经使它变成了美丽的事物。这款3...