当我们使用某个总结的知识点解决了某个问题的时候,我们称其为**证真**,即使用现实的问题来验证了自己理论的正确性,在**证真**的过程中我们学习到了新的知识;当我们用历史总结的某个知识点来再次尝试解决类似问题的时候发生了预期外的错误,我们称其为**证伪**,即我们使用现实的问题证明某个已形成的理论是错误的,此时则是我们知识的提升。所以**证伪比证真更重要**。
在3.3.6小节中我们初次接触了`exports`关键字,并有如下说明:
` ② 导出配置过的RouterModule,其它模块若引用该模块(AppRoutingModule),则将自动引用RouterModule。`
![](https://img.kancloud.cn/f8/d2/f8d2735ad3016998f9b97644ef5a3dc7_935x408.png)
但通过对上个小节的学习我们发现以上说法是错误的。因为其它模块自动导入RouterModule的原因是由于其声明在了`imports`,而非`exports`中。也就是说:即使我们不在app-routing.module.ts中进行`exports`声明,app模块仍然是相当于import了RouterModule的。按这个理论,我们尝试删除app-routing.module.ts中`exports`声明的RouterModule。
app-routing.module.ts
```
@NgModule({
imports: [RouterModule.forRoot(routes)],
exports: []
})
export class AppRoutingModule {
}
```
#### 测试
![](https://img.kancloud.cn/06/d2/06d2009acdf1ecc64849c432fcb4cdf6_1276x113.png)
测试得到如上错误说不认识`router-outlet`这个组件。由于刚刚删除了`exports`中的RouterModule,所以我们能够马上就定位到是由于该操作造成的。当然也可以推测出`router-outlet`这个组件是属于`RouterModule`的。那么按照前面**找谁帮忙前需要引入该服务所在的模块**的理论,我们尝试直接在App模块中引入`RouterModule`试试看。
app.module.ts
```
imports: [
BrowserModule,
AppRoutingModule,
HttpClientModule,
FormsModule,
RouterModule
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule {
}
```
最终的确不报错了。我们认为当前现象最少说明了以下以点:
* [ ] **找谁帮忙前需要引入该服务所在的模块**的理论是正确的。
* [ ] imports进行层级依赖的理论是不完全正确的。
* [ ] exports在层级依赖中起着一定的作用。
## 官方文档
到了这种时候,我们就可以开始尝试使用终极的解决办法**查阅官方文档**了,每当这时候,我们就开始恨自己为什么当初不好好的学习英文的阅读理解部分了。
官方文档对exports这部分进行如下解释:
```
此 NgModule 中声明的一组组件、指令和管道可以在导入了本模块的模块下任何组件的模板中使用。 导出的这些可声明对象就是该模块的公共 API。
The set of components, directives, and pipes declared in this NgModule that can be used in the template of any component that is part of an NgModule that imports this NgModule. Exported declarations are the module's public API.
```
虽然angular有相当优秀的官方文档,但有时候看英文的原文会更有韵味,更能让我们理解其中的含意。
我们好像懂了:
* [ ] 在exports中声明组件A后,表示其它模块在imports本模块后,可以在自己的模块中使用该组件A。
* [ ] 在exports中声明模块A后,表示其它模块在imports本模块后,可以在自己的模块中使用该模块A中所exports的组件。
* [ ] 在imports中声明模块A后,表示该模块可以使用模块A中所exports的内容(可能是本模块组件、也可能是声明的其它模块中的所有exports内容)。
按上述理论重新绘制系统逻辑图如下:
![](https://img.kancloud.cn/62/01/6201ff3d64131205b81ad410c5a2b839_736x429.png)
> 上述的理论也不见得就一定是正确的。但没有任何关系,因为我们的当前理论已经可以证真当前遇到的所有问题了,而这在学习的过程中就足够了。
最后,按上述的理论我们删除掉klass/klass.module.ts中的exports部分。原因是Klass模块未被任何模块imports,当然也就不需要任何的exports了。
klass/klass.module.ts
```
/**
* 班级模块
*/
@NgModule({
declarations: [IndexComponent, AddComponent, EditComponent],
imports: [
CommonModule,
FormsModule,
ReactiveFormsModule,
RouterModule.forChild(routes)
]
})
export class KlassModule {
}
```
最后我们进行系统的测试,以保证当前的变更不对历史的功能造成任何影响 。
# 参考文档
| 名称 | 链接 | 预计学习时长(分) |
| --- | --- | --- |
| 源码地址 | [https://github.com/mengyunzhi/spring-boot-and-angular-guild/releases/tag/step3.4.8](https://github.com/mengyunzhi/spring-boot-and-angular-guild/releases/tag/step3.4.8) | - |
| exports | [https://www.angular.cn/api/core/NgModule#exports](https://www.angular.cn/api/core/NgModule#exports) | 5 |
- 序言
- 第一章:Hello World
- 第一节:Angular准备工作
- 1 Node.js
- 2 npm
- 3 WebStorm
- 第二节:Hello Angular
- 第三节:Spring Boot准备工作
- 1 JDK
- 2 MAVEN
- 3 IDEA
- 第四节:Hello Spring Boot
- 1 Spring Initializr
- 2 Hello Spring Boot!
- 3 maven国内源配置
- 4 package与import
- 第五节:Hello Spring Boot + Angular
- 1 依赖注入【前】
- 2 HttpClient获取数据【前】
- 3 数据绑定【前】
- 4 回调函数【选学】
- 第二章 教师管理
- 第一节 数据库初始化
- 第二节 CRUD之R查数据
- 1 原型初始化【前】
- 2 连接数据库【后】
- 3 使用JDBC读取数据【后】
- 4 前后台对接
- 5 ng-if【前】
- 6 日期管道【前】
- 第三节 CRUD之C增数据
- 1 新建组件并映射路由【前】
- 2 模板驱动表单【前】
- 3 httpClient post请求【前】
- 4 保存数据【后】
- 5 组件间调用【前】
- 第四节 CRUD之U改数据
- 1 路由参数【前】
- 2 请求映射【后】
- 3 前后台对接【前】
- 4 更新数据【前】
- 5 更新某个教师【后】
- 6 路由器链接【前】
- 7 观察者模式【前】
- 第五节 CRUD之D删数据
- 1 绑定到用户输入事件【前】
- 2 删除某个教师【后】
- 第六节 代码重构
- 1 文件夹化【前】
- 2 优化交互体验【前】
- 3 相对与绝对地址【前】
- 第三章 班级管理
- 第一节 JPA初始化数据表
- 第二节 班级列表
- 1 新建模块【前】
- 2 初识单元测试【前】
- 3 初始化原型【前】
- 4 面向对象【前】
- 5 测试HTTP请求【前】
- 6 测试INPUT【前】
- 7 测试BUTTON【前】
- 8 @RequestParam【后】
- 9 Repository【后】
- 10 前后台对接【前】
- 第三节 新增班级
- 1 初始化【前】
- 2 响应式表单【前】
- 3 测试POST请求【前】
- 4 JPA插入数据【后】
- 5 单元测试【后】
- 6 惰性加载【前】
- 7 对接【前】
- 第四节 编辑班级
- 1 FormGroup【前】
- 2 x、[x]、{{x}}与(x)【前】
- 3 模拟路由服务【前】
- 4 测试间谍spy【前】
- 5 使用JPA更新数据【后】
- 6 分层开发【后】
- 7 前后台对接
- 8 深入imports【前】
- 9 深入exports【前】
- 第五节 选择教师组件
- 1 初始化【前】
- 2 动态数据绑定【前】
- 3 初识泛型
- 4 @Output()【前】
- 5 @Input()【前】
- 6 再识单元测试【前】
- 7 其它问题
- 第六节 删除班级
- 1 TDD【前】
- 2 TDD【后】
- 3 前后台对接
- 第四章 学生管理
- 第一节 引入Bootstrap【前】
- 第二节 NAV导航组件【前】
- 1 初始化
- 2 Bootstrap格式化
- 3 RouterLinkActive
- 第三节 footer组件【前】
- 第四节 欢迎界面【前】
- 第五节 新增学生
- 1 初始化【前】
- 2 选择班级组件【前】
- 3 复用选择组件【前】
- 4 完善功能【前】
- 5 MVC【前】
- 6 非NULL校验【后】
- 7 唯一性校验【后】
- 8 @PrePersist【后】
- 9 CM层开发【后】
- 10 集成测试
- 第六节 学生列表
- 1 分页【后】
- 2 HashMap与LinkedHashMap
- 3 初识综合查询【后】
- 4 综合查询进阶【后】
- 5 小试综合查询【后】
- 6 初始化【前】
- 7 M层【前】
- 8 单元测试与分页【前】
- 9 单选与多选【前】
- 10 集成测试
- 第七节 编辑学生
- 1 初始化【前】
- 2 嵌套组件测试【前】
- 3 功能开发【前】
- 4 JsonPath【后】
- 5 spyOn【后】
- 6 集成测试
- 7 @Input 异步传值【前】
- 8 值传递与引入传递
- 9 @PreUpdate【后】
- 10 表单验证【前】
- 第八节 删除学生
- 1 CSS选择器【前】
- 2 confirm【前】
- 3 功能开发与测试【后】
- 4 集成测试
- 5 定制提示框【前】
- 6 引入图标库【前】
- 第九节 集成测试
- 第五章 登录与注销
- 第一节:普通登录
- 1 原型【前】
- 2 功能设计【前】
- 3 功能设计【后】
- 4 应用登录组件【前】
- 5 注销【前】
- 6 保留登录状态【前】
- 第二节:你是谁
- 1 过滤器【后】
- 2 令牌机制【后】
- 3 装饰器模式【后】
- 4 拦截器【前】
- 5 RxJS操作符【前】
- 6 用户登录与注销【后】
- 7 个人中心【前】
- 8 拦截器【后】
- 9 集成测试
- 10 单例模式
- 第六章 课程管理
- 第一节 新增课程
- 1 初始化【前】
- 2 嵌套组件测试【前】
- 3 async管道【前】
- 4 优雅的测试【前】
- 5 功能开发【前】
- 6 实体监听器【后】
- 7 @ManyToMany【后】
- 8 集成测试【前】
- 9 异步验证器【前】
- 10 详解CORS【前】
- 第二节 课程列表
- 第三节 果断
- 1 初始化【前】
- 2 分页组件【前】
- 2 分页组件【前】
- 3 综合查询【前】
- 4 综合查询【后】
- 4 综合查询【后】
- 第节 班级列表
- 第节 教师列表
- 第节 编辑课程
- TODO返回机制【前】
- 4 弹出框组件【前】
- 5 多路由出口【前】
- 第节 删除课程
- 第七章 权限管理
- 第一节 AOP
- 总结
- 开发规范
- 备用