写点什么

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

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

关注

评论

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

30分钟成为Contributor|共建测试子系统,赋能提升项目代码质量

OpenHarmony开发者

OpenHarmony

如何提升IT对业务发展的贡献度

凌晞

数字化 构架 IT管理

TDesign 品牌价值观

TDesign

如何通过FinOps实现企业云成本优化?——信通院《中国FinOps产业发展现状研究报告》解读

星汉未来

云原生 k8s IT FinOps 云成本优化

“以太坊杀手” Polkadot 何以在一众公链中脱颖而出

One Block Community

区块链 去中心化 跨链技术 波卡生态

有一说一!阿里巴巴8月最新发布的这份Spring Cloud Alibaba学习笔记,几乎涵盖了其所有操作

程序员小毕

Java 程序员 面试 微服务 SpringCloud

史上最全的Java并发系列之Java中的并发工具类

自然

多线程 并发 8月月更

SpringMVC(五、AOP)

开源 springmvc spring aop 8月月更

深度学习公式推导:从神经元出发

老崔说架构

解析 RocketMQ 业务消息——“事务消息”

阿里巴巴云原生

阿里云 RocketMQ 云原生 消息队列

直播预告 | 如何从 0 到 1 打造敏捷团队?

Authing

知识管理的成熟,需要具备什么条件?

Baklib

开源一夏 | 你不能错过的 Go 强大图形化测试工具

宇宙之一粟

开源 测试框架 testing Go 语言 8月月更

落地DevOps,探索高效研发运营一体化解决方案

云智慧AIOps社区

DevOps 质量管理 代码管理 自动化运维 研发效率

开源一夏 | 实战之AngularJS 的Scope和Service的深入应用心得

恒山其若陋兮

开源 8月月更

一文读懂BizDevOps:数字化转型下的技术破局

阿里云云效

技术 DevOps 云原生 数字化转型 开发.

A tour of gRPC:07 - gRPC bidirectional straming 双向流

BUG侦探

gRPC RPC protocolBuffer

我们总结了 3 大使用建议,并首次公开 Nacos 3.0 规划图 | Nacos 开源 4 周年

阿里巴巴云原生

阿里云 开源 微服务 云原生 nacos

实证与虚无,抽象和具象,Go lang1.18入门精炼教程,由白丁入鸿儒,Go lang接口(interface)的使用EP08

刘悦的技术博客

Go 编程 教程 Go web 教程分享

化整为零优化重用,Go lang1.18入门精炼教程,由白丁入鸿儒,go lang函数的定义和使用EP07

刘悦的技术博客

Go 教程 Go web 教程分享

基于AQS实现自己的同步工具

自然

多线程 并发 8月月更

史上最全的Java并发系列之Java中的13个原子操作类

自然

多线程 并发 Java core 8月月更

业内首份!博睿数据入选中国信通院《中国AIOps现状调查报告(2022)》

博睿数据

博睿数据 智能运维AIOps Swift AI

开源一夏 | 拥抱新技术?你需要考虑的方面

微枫Micromaple

开源 经验总结 技术栈 技术分享 8月月更

MQTT over QUIC:下一代物联网标准协议为消息传输场景注入新动力

EMQ映云科技

车联网 物联网 mqtt emqx 8月月更

在知识经济的年代,让你学会如何经营好自己的知识

Baklib

参与openEuler社区不到1年,我成为了社区Maintainer……

openEuler

openEuler 开源社区 社区运营 #开源

软件架构治理 之 如何度量软件架构

码猿外

架构设计 软件架构治理

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