写点什么

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:466781
用户头像

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

关注

评论

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

告诉你!操作系统是个大骗子!

博文视点Broadview

企业数字化转型与SAP云平台

汪子熙

SAP 11月日更 SAP数字化转型 SAP云平台

虚拟机与容器的混合管理实践

安第斯智能云

Kubernetes 后端 云化底层的那些事

到底什么样的ABAP系统能运行Fiori应用

汪子熙

SAP abap Fiori 11月日更

GaussDB NoSQL架构设计分享

华为云开发者联盟

nosql 云原生 GaussDB 超融合 多模数据库

时序序列分类算法概述

云智慧AIOps社区

大数据 算法 时间序列 智能运维

亿磐获评《新闻周刊》2021年度最受雇员喜爱工作场所

InfoQ_434670063458

亿磐 EPAM 新闻周刊

博文推荐|Pulsar 存储空间不释放的问题分析与解决方法

Apache Pulsar

架构 云原生 Apache Pulsar 技术干货 BookKeeper 存储空间

一个站点不够学?那就在用Python增加一个采集目标,一派话题广场+某金融论坛话题广场爬虫

梦想橡皮擦

11月日更

一图看懂 Apache ShardingSphere 5.0.0

SphereEx

数据库 开源 ShardingSphere

TDengine助力曲靖卷烟厂有效提升时序数据存取效率

TDengine

数据库 tdengine 后端

AI运动:阿里体育端智能最佳实践

阿里巴巴终端技术

最佳实践 端智能

译文|选择 Apache Pulsar 而非 Kafka 的 10 个理由

Apache Pulsar

kafka 架构 云原生 中间件 Apache Pulsar

Go iota 原理和源码剖析

编程宝库

博文推荐|Pulsar 的消息存储机制和 Bookie 的 GC 机制原理

Apache Pulsar

存储 Apache Pulsar 消息中间件 Apache BookKeeper bookie

She Builds Summit|邀您一同感受她的科技力量!

SphereEx

开源 AWS SphereEx 潘娟

Kafka中的数据不丢失机制及CAP理论详解

五分钟学大数据

11月日更

APP上运行小程序的混合移动研发模式

Speedoooo

容器 ios开发 APP开发

潜藏在手机中的新威胁:免安装应用安全指北

安第斯智能云

安全 移动端

云小课 | 网站接入WAF失败怎么办?看这里就够了

华为云开发者联盟

华为云 Web应用防火墙 接入WAF失败 故障排查

CSS奇技淫巧之滤镜

Augus

CSS 11月日更

技术分享| 如何做一款容纳百人的视频会议?

anyRTC开发者

音视频 WebRTC 视频会议 实时通信 视频通话

巅峰对话在线研讨 Q&A:Oracle Database 21c vs openGauss 2.0新特性解读和架构演进

墨天轮

数据库 oracle opengauss

纪念林徽因

containerd镜像层过大问题

ilinux

博文推荐 | 一文带你看懂 Pulsar 的消息保留和过期策略

Apache Pulsar

架构 云原生 中间件 干货 Apache Pulsar

ETL和数据建模

大数据技术指南

11月日更

Python 中的反转字符串:reversed()、切片等

华为云开发者联盟

Python 切片 反转字符串 reversed 反转

Black Hat Europe 2021议题解读:Wi-Fi Mesh中的安全攻击面

百度安全

Black Hat Wi-Fi Mesh

论文解读丨无监督视觉表征学习的动量对比

华为云开发者联盟

视觉 无监督学习 无监督视觉 表征学习 动量对比

这可能是,Flutter 中最“强悍”的内存泄漏检测方案......

阿里巴巴终端技术

flutter ios android 内存泄漏

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