写点什么

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

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

关注

评论

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

从“人居美学”的理念出发,木莲庄酒店很不一样

InfoQ_967a83c6d0d7

PostgreSQL函数、索引和视图操作Demo展示

王坤祥

postgresql

Tencent AI Lab 日常实习生招聘

InfoQ_d00afcd122a8

招聘

百度联合发布全球最大中文自然语言处理数据共建计划“千言”

百度大脑

人工智能 百度 nlp 百度大脑

青山不改,绿水长流

escray

学习 面试

ARTS 挑战打卡第十五周(200817-200823)

老胡爱分享

ARTS 打卡计划

3D渲染集群,你了解多少?

华为云开发者联盟

容器 华为云 集群 3D渲染 3D模型

兼具艺术情怀和舒适旅居体验,木莲庄做了什么

InfoQ_967a83c6d0d7

话题讨论 | 作为一名程序员,沟通能力是否重要?

InfoQ写作社区官方

程序员 写作平台 话题讨论

永续合约交易系统源码开发,合约交易所开发

13530558032

Python中的global关键字的用法以及如何设置项目级的Global变量

陈磊@Criss

一键超频 双C Carry,普通用户如何挖潜英特尔十代酷睿?

E科讯

Java中的volatile关键字

王坤祥

Java volatile

大数据思维与运用

石云升

大数据 数据建模 数据驱动

工作就是解决一个又一个问题

escray

学习 面试

SpreadJS 纯前端表格控件应用案例:PtLims云平台

葡萄城技术团队

乘风人脸识别来了,企业能得到什么?

ToB行业头条

人工智能 人脸识别 百度大脑

有它的加持,单机玩转百亿大数据不是梦!

易观大数据

CECBC区块链专委会副主任吴桐主讲成都市委宣传部区块链和数字

CECBC

区块链 数字货币

云中谁寄锦书来,免费生成一封七夕情书吧

郭旭东

阿里云 七夕 云效

SpreadJS 纯前端表格控件应用案例:物业行业全面预算管理系统

葡萄城技术团队

游戏运营方法论

石云升

游戏化运营 运营创新

oeasy教你玩转linux010104灵魂之问whatis

o

SpreadJS 纯前端表格控件应用案例:资料填报系统

葡萄城技术团队

阿里为什么推荐使用LongAdder,而不是volatile?

王磊

Java 性能 volatile

ARTS 挑战打卡第十四周(200810-200816)

老胡爱分享

ARTS 打卡计划

SpreadJS 纯前端表格控件应用案例:立信智能审计云平台(SACP)

葡萄城技术团队

SpreadJS 纯前端表格控件应用案例:实验室信息化管理系统

葡萄城技术团队

Python3 单例模式

王坤祥

Python Singleton

用Emoji解释编程语言中的map、filter、reduce

王坤祥

Python Python PEP

为什么会出现零代码开发平台?

代码制造者

编程语言 低代码 企业信息化 零代码 编程开发

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