AICon上海|与字节、阿里、腾讯等企业共同探索Agent 时代的落地应用 了解详情
写点什么

Angular 的模块间通信

  • 2017-03-15
  • 本文字数:3552 字

    阅读完需:约 12 分钟

关键点

  • Angular 是用基于模块的应用程序架构构建的,所以是有可能在模块之间传输数据的;
  • 你可以通过 @Input() 把数据传入子模块;
  • 你可以通过 @Output() 从子模块捕获数据;
  • 随着应用程序的发展,父子模块之间的通信会越来越困难;
  • 你可以通过 ngrx 之类的方法用外部数据库来构建大规模应用程序;

模块是 Angular 的构建单元,Angular 应用程序的所有可视化元素也是由模块构建的。基于模块的架构的一个重大好处在于,与 JavaScript 函数非常相像,如果一块代码变得过于复杂,或者承担了过多责任,你就可以把它打散,让每一段代码都只做一件事。

也就是说,当我们把模块拆散成更小的模块时,我们就要确保它们可以把数据传来传去。到那时候,恰当地模块间通信机制就成了我们应用程序的基础,可以让所有的数据都保持同步状态。幸运的是,Angular 给我们提供了这样的工具来完成这件事。

如上图所示,我们可以在 AppComponent 之下构建整个应用程序,但这样做会让那个模块承担过多的责任。在基于模块的架构里,大家公认比较好的实践是把模块拆散,好让它们都只承担单一的责任。

将数据传给模块

在 Angular 中,当一个父模块要把数据传入子模块中时,我们可以使用 @Input。假设我们现在要构建一个程序,在页面上显示评论。AppComment 将负责加载所有的评论内容,我们会把每条评论数据都发送给评论模块。

我们将调用:

复制代码
@Input() comment

把一个 comment 参数传入子模块。下面就是整块模块代码的样子:

复制代码
@Component({
selector: 'comment',
templateUrl: './comment.component.html',
styleUrls: ['./comment.component.css']
})
export class CommentComponent {
@Input() comment;

现在我们可以从我们代码其它部分调用这个模块,并把它需要的数据传入这个模块了。这块代码看起来会像这样:

复制代码
<comment [comment]="comment"></comment>

理解语法

首先,我们有一个模块选择器:

复制代码
<comment></comment>

如果你以前用过 Angular,这个语法看起来应该是很熟悉的。

其次是属性绑定:[comment]。这把我们元素的属性括起来的中括号第一眼看上去似乎有些令人费解。事实上,并不需要它们来帮着把数据传到各个模块中,但没有它们,我们就只能把一个普通的文本字符串传入模块的 @Input()。中括号可以告诉 Angular,这是一个属性绑定以及传给这个变量的值,所以这样就可以把动态数据值插入,传给各模块,而不只是传过去一个字符串了。

最后,我们在“comment”的属性绑定之后有了属性的值。这一块就是告诉 Angular 要注意这个“comment”属性,并且把它传给我们的评论模块。

将概念组合起来

为了像我们在上图中所显示地展示一个评论的列表,我们可以把许多 Angular 概念组合起来,包括 *ngFor。咱们假设我们可以用一个名为 this.comments 的属性的方式把评论数据作为我们模块类的一部分导入。

复制代码
<comment
*ngFor="let comment of comments"
[comment]="comment"></comment>

最终结果看起来像是这样的:

使用评论模块的评论列表

捕获子模块事件

当我们知道了该如何把数据传入评论模块之后,那该怎样把一个模块删掉,并因此从列表中消失呢?这个看起来就有些诡异,因为数据是保存在评论模块的父模块——AppComponent 之中的。

解决这个问题的办法就是调用名为 @Output() 的方法,它可以利用 EventEmitter,让子模块发射出父模块可以捕获的事件。

让子模块可以与父模块通信的第一步就是用 @Output() 描述符给我们的模块加上一个新的类属性。

复制代码
@Component({
selector: 'comment',
templateUrl: './comment.component.html'
})
export class CommentComponent {
@Input() private comment;
@Output() private onDelete = new EventEmitter();
deleteComment() {
this.onDelete.emit(this.comment);
}
}

在这个 CommentComponent 的代码里,我们可以在 delete 按钮被按下时调用 deleteComment() 方法,让我们可以捕获这些从父模块发过来的事件。

复制代码
<button (click)="deleteComment()">Delete</button>

从父模块中捕获事件

在 AppComponent 中,我们需要有个新方法来处理删除评论的行为。这个方法会收到:

复制代码
@Component({
selector: 'app-root',
templateUrl: './app.component.html'
})
export class AppComponent {
/**[code omitted]**/
onCommentDelete(comment) {
// logic to remove comment from comments array
}
}

在我们的视图中,我们只需要告诉 Angular 当 onDelete 事件被触发时,要调用 onCommentDelete() 方法。

复制代码
<comment
*ngFor="let comment of comments"
[comment]="comment"
(onDelete)="onCommentDelete($event)"></comment>

当我们的删除功能就绪之后我们的程序看起来是这样的:

现在有了 Angular 的 @Output() 描述符,我们就可以删除评论了。

还有另外一种获取数据的方法

到目前为止,我们还只讨论了父 - 子模块之间进行模块通信的方法。尽管现在这种通信方法已经可以满足大多数人的需求,但随着应用程序规模的增长,继续维护父子之模块间的数据通信模式就会越来越困难。对于大型程序来说,用数据库来减轻单个模块的工作量的方法通常都是有效的。数据库可以做为中心存储,在需要时可以被应用程序的各个单独模块调用。单个模块可以直接连上数据库,只使用它们所需要的那一部分数据,而不是把数据手动地顺序按子模块链不断传下去,从而减轻了父模块和子模块所承担的把数据传来传去的责任。

如果你熟悉 React,就会知道这正是 Redux 要解决的问题。可是有了 Angular,我们就有了另一个可用的名为 ngrx 的库,而这恰好是受 Redux 启发而成的。在这两个库之间有些关键不同:类型和观测值。ngrx 库对 TypeScript 生态系统的依赖非常重,所以会比 Redux 更加繁冗,但却让调试和调查问题变得更容易。

在用 ngrx 时,我们的状态是一个单独不变的数据结构。为了让我们的状态可变,我们可以用行为的方式来调用函数。反之,这些行为可以告诉我们的由单纯的函数组成的处理逻辑,哪一块的状态是可变的。这样,我们的应用程序就可以有状态的新版本数据了,而那些关注这些数据的模块就可以马上收到新数据。当这些模块收到新数据之后,它们会被自动重新展示,让我们的视图始终与数据库中的数据保持一致。

这对于开发又意味着什么?

这从概念上表明,是有可能从系统外部把数据直接传给某个模块,而不必通过父子关系的,这在事实上对于开发来说意味着什么呢?

如果我们想在两个不同的地方显示评论的条数呢?仅仅依靠模块的层级架构是很难实现的,这种情况下使用 ngrx 的中心存储就很有效了。

在较大的程序内部,把数据直接传给某个模块可以解决很多问题。随着各个模块要处理越来越多的任务,再要增加类似跟踪把数据传入子模块这类额外的任务就会变得复杂。当不能把传输数据的负担交给外部数据库时,维护大型 Angular 程序时保持头脑清醒就很必要了。

在上面的例子里,一个外部数据库(图中没有画出来)可以用来把信息传给 SidebarComponent,表示有多少评论可用,“有两条评论”,同时还把真实的评论内容发给 CommentList 模块。这些数据完全绕过了父模块 AppComponent。

快速回顾一下 Angular 的模块通信机制

读过上文,我们已经对 Angular 的模块之间如何通信有了一些了解。我们知道了怎样把数据从一个父模块传入一个子模块,而且我们也知道了怎么使用 Angular 的 @Input() 描述符。

我们也知道了怎样使用 @Output() 描述符,以及如何使用 EventEmitter 来把数据从一个子模块发回给一个父模块。

除此之外,当应用程序规模增长之后,我们也知道了如何用 ngrx 来减轻父子关系的压力。通过使用一个单独的数据库,我们就可以让各个模块自己获取数据,而不必通过多层子模块居中传递来把数据传到真正的目的地去。这就让维护大规模应用程序变得容易了。这些原理也适用于 React 之类的库,他们也用非常类似的办法解决了相同的问题。

你可以在这个链接上看到我们的演示程序视频,相应的代码也可以在这里下载。

如何进一步学习

如果你有兴趣对Angular 了解得更多些,请一定看看Code School 的“ Accelerating through Angular 2 ”课程,再看看 Gregg Pollack 和我自己的“ build an Angular 2 app with component interaction & routing ”课程(仅供 Code School 会员学习)。

另外,可以再看看 Angular 关于模块交互的文档,进一步了解底层机制,以及随着框架的快速演进,最新的语法和最佳实践。

最后,你也可以再读读 ngrx 官方网站上的 ngrx 文档,或者在这里看看我用ngrx 构建的一个示例程序。

关于作者

Sergio Cruz是 Code School 的一位应用程序开发者和咨询师,兴趣广泛,尤其是 JavaScript。最近,他新讲授了一些 Code School 的 React 课程,“ Powering up With React ”。当他不在 Code School 敲代码时,他也会去 ng-conf 或 OSCON 等 JavaScript 相关的会议上作作讲座。

阅读英文原文 Getting Components to Communicate in Angular

2017-03-15 18:466911
用户头像

发布了 152 篇内容, 共 74.2 次阅读, 收获喜欢 64 次。

关注

评论

发布
暂无评论
发现更多内容

用乐高玩转Scrum,轻松拿CSM证书

ShineScrum

Scrum ScrumMaster CSM 乐高

阿里云架构师梁旭:MES on 云盒,助力客户快速构建数字工厂

阿里云弹性计算

最佳实践 数字化转型 制造业 mes 云盒

Electron框架XSS TO RCE简析

美创科技

漏洞

变量 var const let 的区别

大熊G

JavaScript 前端 前端教程 6月月更

最强分布式事务框架是怎么炼成的?

峨嵋闲散人

分布式事务 云原生 分库分表 无侵入 dbmesh

Python的教程

芯动大师

Python编程 6月月更

互联网拓扑是怎样构成的?又代表了什么?

郑州埃文科技

互联网拓扑 网络空间地图

CC2530 GPIO口输出配置说明​

DS小龙哥

6月月更

火遍全网的AI给老照片上色,这里有一份详细教程!

博文视点Broadview

私有化IM即时通讯怎样在保障企业安全下提高效率?

BeeWorks

发布一个轻量级的 Elasticsearch 压测工具 - Loadgen

极限实验室

elasticsearch 极限实验室 loadgen 压测工具 esrally

web前端培训如何在 H5 网页中实现扫码功能

@零度

前端开发

推进流程挖掘技术发展,信通院首轮流程挖掘评测预报名正式启动

王吉伟频道

RPA 信通院 流程挖掘 流程挖掘评测 行业标准

LLVM之父Chris Lattner:模块化设计决定AI前途,不服来辩

OneFlow

机器学习 深度学习 AI

2022年SaaS的10个有趣趋势

小炮

架构实战营模块 7 作业

热猫

Microsoft  Office  MSDT 代码执行漏洞

郑州埃文科技

网络安全 漏洞分析 漏洞缓解

区块链技术促进医药冷链物流更加可控和智能化

CECBC

实时特征计算平台架构方法论和基于 OpenMLDB 的实践

第四范式开发者社区

机器学习 数据库 AI 特征平台 特征工程

Kubernetes容器网络及Flannel插件详解

巨子嘉

云原生

Java中的阻塞队列

急需上岸的小谢

6月月更

写Python爬虫,服务器返回数据加密了,套路解决法~,出版社,出版社

梦想橡皮擦

6月月更

博睿数据荣获优炫软件产品兼容互认证书和海量数据兼容互认证书

博睿数据

智能运维 博睿数据 产品兼容 数据兼容

专为云原生、微服务架构而设计的链路追踪工具 【SkyWalking介绍及搭建】

掂掂三生有幸

云原生 Skywalking ,docker 微服务框架 6月月更

NFT,元宇宙的通行证

CECBC

Django基础-1

zyf

django 6月月更

场景驱动的特征计算方式OpenMLDB,高效实现“现算先用”

第四范式开发者社区

人工智能 机器学习 数据库 开源 实时计算

【直播回顾】Hello HarmonyOS进阶课程第五课——原子化服务

HarmonyOS开发者

HarmonyOS

Angular的模块间通信_JavaScript_Sergio Cruz_InfoQ精选文章