# 初始化组件 是的,所有的功能在开发前都离不开初始化的工作。只有当组件被初始化完成后,整个功能才能抽象变更为具体。团队内部以及我们与甲方的交流也因此会加的顺畅,这保证了我们将开发一个与甲方预期一致的系统。 ## 新建模块 我们在第二章曾经对模块进行了如下总结: ![image-20210228173021966](https://img.kancloud.cn/c6/77/c6778e95733b9e82f3a492baa4baeb92_1410x542.png) 可见模块与模块间是通过`imports`来配合完成工作的。 在教师管理一章中,我们使用了Angular初始化的自带模块`AppModule`,本节我们初始化一个单独用于班级管理的模块`ClazzModule`,然后在此模块中添加教师管理中的新增、列表、编辑等组件。这样的做当前的好处是可以使原本越来越乱的目录结构变得稍微的清爽一些。 我们使用shell来到src/app文件夹,执行`ng g module clazz`命令来快速的创建一个模块: ```bash panjie@panjies-Mac-Pro app % pwd /Users/panjie/github/mengyunzhi/angular11-guild/first-app/src/app panjie@panjies-Mac-Pro app % ng g module clazz CREATE src/app/clazz/clazz.module.ts (191 bytes) ``` 该命令将自动为我们在app文件夹下创建一个新的文件夹clazz,并在该文件夹下自动创建一个`clazz.module.ts`文件。 ```bash panjie@panjies-Mac-Pro app % tree clazz clazz └── clazz.module.ts ``` 文件内容如下: ```typescript import {NgModule} from '@angular/core'; import {CommonModule} from '@angular/common'; @NgModule({ declarations: [], imports: [ CommonModule ] }) export class ClazzModule { } ``` 如上所示,类`ClazzModule`是使用了注解`@NgModule` ,因此被Angular识别为`模块`。 ## 新建组件 接着使用shell进入src/app/clazz文件夹,执行创建组件命令: ```bash panjie@panjies-Mac-Pro clazz % pwd /Users/panjie/github/mengyunzhi/angular11-guild/first-app/src/app/clazz panjie@panjies-Mac-Pro clazz % ng g c add CREATE src/app/clazz/add/add.component.css (0 bytes) CREATE src/app/clazz/add/add.component.html (18 bytes) CREATE src/app/clazz/add/add.component.spec.ts (605 bytes) CREATE src/app/clazz/add/add.component.ts (263 bytes) UPDATE src/app/clazz/clazz.module.ts (250 bytes) ``` **注意**:必须确保shell所在文件夹位置为src/app/clazz。 Angular聪明的在当前文件夹创建了班级add组件,并将其声明到了clazz.module.ts中,以表明班级add组件为班级模块的一部分。 ### V层 模板代码如下: ```html b/first-app/src/app/clazz/add/add.component.html <form class="container-sm"> <div class="mb-3 row"> <label for="name" class="col-sm-2 col-form-label">名称</label> <div class="col-sm-10"> <input type="text" class="form-control" name="name" id="name"> </div> </div> <div class="mb-3 row"> <label for="teacher" class="col-sm-2 col-form-label">班主任</label> <div class="col-sm-10"> <select id="teacher" class="form-control" > <option>张三</option> <option>李四</option> </select> </div> </div> <div class="mb-3 row"> <div class="col-sm-10 offset-2"> <button class="btn btn-primary">保存</button> </div> </div> </form> ``` 使用`ng t`后启用单元测试并查看效果如下: ![image-20210318100250221](https://img.kancloud.cn/2b/f8/2bf859a8d1b3a66075b51eada441a5ad_1518x386.png) ### CV交互 select选择框如何做我们还不太清楚,但部分的功能我们还是学习过的,让我们简单来实现这些功能。在CV的交互中,至于先写哪个后写哪个完全看自己的习惯,喜欢先写哪个都可以,比如我在这比较喜欢先写V层: ```html +++ b/first-app/src/app/clazz/add/add.component.html @@ -1,14 +1,15 @@ -<form class="container-sm"> +<pre>{{clazz | json}}</pre> 👈 +<form class="container-sm" (ngSubmit)="onSubmit()"> <div class="mb-3 row"> <label for="name" class="col-sm-2 col-form-label">名称</label> <div class="col-sm-10"> - <input type="text" class="form-control" name="name" id="name"> + <input type="text" class="form-control" [(ngModel)]="clazz.name" name="name" id="name"> </div> </div> <div class="mb-3 row"> <label for="teacher" class="col-sm-2 col-form-label">班主任</label> <div class="col-sm-10"> - <select id="teacher" class="form-control" > + <select id="teacher" class="form-control"> <option>张三</option> <option>李四</option> </select> ``` 在这增加一个`<pre>`标签会有意想不到的收获。 再写C层: ```typescript +++ b/first-app/src/app/clazz/add/add.component.ts @@ -1,4 +1,4 @@ -import { Component, OnInit } from '@angular/core'; +import {Component, OnInit} from '@angular/core'; @Component({ selector: 'app-add', @@ -6,10 +6,17 @@ import { Component, OnInit } from '@angular/core'; styleUrls: ['./add.component.css'] }) export class AddComponent implements OnInit { + clazz = { + name: '' + }; - constructor() { } + constructor() { + } ngOnInit(): void { } + onSubmit(): void { + console.log('submit'); + } } ``` 这时候聪明的WebStorm将会在V层给我们一些错误的提示: ![image-20210318102928616](https://img.kancloud.cn/22/88/228847d93dc0565616ace4837588a522_1744x318.png) ![image-20210318102943663](https://img.kancloud.cn/63/f9/63f9c76628747431e89abca17e15e0c8_1492x230.png) 错误的意思都是在说没有任何的`directive`来支持我们刚刚写的代码,比如:`(ngSubmit)`以及`[(ngModel)]`。事实也是这样,由于`(ngSubmit)`以及`[(ngModel)]`并不存在于html语言中,所以要想使用其生效,则需要有一个叫做`directive`的东西来支持这样功能。而前我们提及过的`FromModule`便是能提供支持`(ngSubmit)`以及`[(ngModel)]`的模块。 模块与模块的交流位于模块的`imports`,为些我们将`FormModule`添加到当前组件所在的ClazzModule中: ```typescript --- a/first-app/src/app/clazz/clazz.module.ts +++ b/first-app/src/app/clazz/clazz.module.ts @@ -1,12 +1,14 @@ import {NgModule} from '@angular/core'; import {CommonModule} from '@angular/common'; import { AddComponent } from './add/add.component'; +import {FormsModule} from '@angular/forms'; @NgModule({ declarations: [AddComponent], imports: [ - CommonModule + CommonModule, + FormsModule ] }) export class ClazzModule { ``` 此时V层的错误消失。其实此时`ng t`所依赖的动态测试模块也面临着这个错误,只是WebStorme还没有智能到能检测出该错误,所以我们手动添加一下: ```typescript --- a/first-app/src/app/clazz/clazz.module.ts +++ b/first-app/src/app/clazz/clazz.module.ts @@ -1,12 +1,14 @@ import {NgModule} from '@angular/core'; import {CommonModule} from '@angular/common'; import { AddComponent } from './add/add.component'; +import {FormsModule} from '@angular/forms'; @NgModule({ declarations: [AddComponent], imports: [ - CommonModule + CommonModule, + FormsModule ] }) export class ClazzModule { ``` 至此大多数的初始化工作,就完成了。我们最后在单元测试中加入**自动检测变更**的代码后,当前的组件便会在单元测试中动起来了。 ## Select 当领导给我们分配一些新的任务时,**我不会**三个字永远不要轻易的就说出口。在现有水平的基础上,合理的利用网上的学习资源,绝对能解决大多数的问题。即使最后没有得到解决的答案,基于当前自己找到的资源去和领导沟通也远胜过**我不会**三个字。 虽然我们还没有学习过如何使用Select,但我相信你当前一定有能力解决这个问题。Select使用方式与普通的input非常的相似:在元素上增加`name`属性并直接应用`([ngModel])`: ```html +++ b/first-app/src/app/clazz/add/add.component.html @@ -9,9 +9,11 @@ <div class="mb-3 row"> <label for="teacher" class="col-sm-2 col-form-label">班主任</label> <div class="col-sm-10"> - <select id="teacher" class="form-control"> - <option>张三</option> - <option>李四</option> + <select id="teacher" class="form-control" + name="teacher" + [(ngModel)]="clazz.teacherId"> + <option [ngValue]="1">张三</option> 👈 + <option [ngValue]="2">李四</option> </select> </div> </div> ``` **注意**:这里使用`[ngValue]`而非`value`,在angular中`[xx]="yyy"`中的yyy表示typescript中的变量或值,我们在这里需要的值是number类型的`1`, `2`,而非字符串类型的`'1'`,`'2'`。如果你学过一些数据库的相关知道,一定知道数字1,2,3与字符1,2,3的区别。如果使用的是value而非`[ngValue]`,则将得到字符串`1`,`2`。 接着在C层中同步为clazz添加一个teacherId字段: ```typescript +++ b/first-app/src/app/clazz/add/add.component.ts @@ -7,7 +7,8 @@ import {Component, OnInit} from '@angular/core'; }) export class AddComponent implements OnInit { clazz = { - name: '' + name: '', + teacherId: null as unknown as number }; constructor() { ``` 测试如下: ![image-20210318110323682](https://img.kancloud.cn/c0/9d/c09d754cd42157c3ba1634737608974c_1574x520.png) 如果我们在`option`元素上,使用了`value`而非`[ngValue]`,则将得到字符类型的`'1'`: ![image-20210318110637762](https://img.kancloud.cn/4c/1f/4c1f10378e055e7ecec5ec918214f2e3_1536x418.png) 而这并不是我们想要的。 ## 本节作业 新增班级的API为: ```bash POST /clazz ``` | **类型Type** | **名称Name** | **描述Description** | **类型Schema** | | :----------- | :----------- | :------------------ | :----------------------------------------------------------- | | Body | clazz | 班级 | `{name: string, teacher: {id: number}}` | | Response | | 响应 | `{id: number, name: string, createTime: number, teacher: {id: number, name: string}}` | 请求Body`{name: string, teacher: {id: number}}`中的teacher属性的类型是一个对象,该对象有一个id属性,类型为number。在响应信息中,给出了保存后的班级ID,以及班级对应的teacher对象基本信息。 1. 请尝试在`ng t`的情况下完成它。 2. 以下信息可能会给问题的解决带来一些思路: 1. `ng t`下报401的错误原因为何? 2. 如何在新建班级前完成用户的登录? | 名称 | 链接 | | ---------------------- | ------------------------------------------------------------ | | 本节源码(包含本节答案) | [https://github.com/mengyunzhi/angular11-guild/archive/step6.1.1.zip](https://github.com/mengyunzhi/angular11-guild/archive/step6.1.1.zip) |