# 手写组件 以往的组件我们都是使用的angular cli来自动生成,本节我们手动创建一个: ## Welcome组件 我们需要一个欢迎组件来对登录成功后的用户显示欢迎信息,为此在`src/app`文件夹中创建`welcome.component.ts`文件: ```bash panjiedeMacBook-Pro:app panjie$ pwd /Users/panjie/github/mengyunzhi/angular11-guild/first-app/src/app panjiedeMacBook-Pro:app panjie$ tree -L 1 . ├── add ├── app-routing.module.ts ├── app.component.css ├── app.component.html ├── app.component.spec.ts ├── app.component.ts ├── app.module.ts ├── edit ├── entity ├── index ├── login ├── personal-center ├── welcome.component.ts 👈 ├── x-auth-token.interceptor.spec.ts └── x-auth-token.interceptor.ts 6 directories, 9 files ``` 接着打开该文件,简单写些组件初始化的注释: ```typescript +++ b/first-app/src/app/welcome.component.ts @@ -0,0 +1,6 @@ +// 定义类 +// 使用注解来标明它是一个组件 +// 将组件加入模块 +// 为组件设置一个selector,以使得其它组件能够使用它 +// 为组件设置V层模板 +// 新建个测试文件来测试组件 ``` 接下来分步完成任务: ## 定义类 ```typescript +++ b/first-app/src/app/welcome.component.ts @@ -1,4 +1,7 @@ // 定义类 +export class WelcomeComponent { + +} // 使用注解来标明它是一个组件 // 将组件加入模块 // 为组件设置一个selector,以使得其它组件能够使用它 ``` 此时应该注意两点: - 类名应该与文件名保持一致,且以`Component`结尾。 - 必须加入`export`以使该类能够被其它文件引用 ## 加注解 ```typescript +++ b/first-app/src/app/welcome.component.ts @@ -1,8 +1,13 @@ // 定义类 +import {Component} from '@angular/core'; + +// 使用注解来标明它是一个组件 +@Component({ + +}) export class WelcomeComponent { } -// 使用注解来标明它是一个组件 // 将组件加入模块 // 为组件设置一个selector,以使得其它组件能够使用它 // 为组件设置V层模板 ``` `@Component`位于`'@angular/core'`中,其接收的参数类型为**对象**。 注解是注释的一种延伸,使用`@`关键字打头。相对于普通的注释,注解不但可以起到对类、属性、方法等的说明作用,而且可以为其设置一些属性或其它更高级的做法。在Angular中,某个类是一个组件还是一个模块,都是由其类上对应的注解实现的。 比如我们在此使用`@Component`将`WelcomeComponent`类声明为一个Angular组件。如果没有此注解,则`WelcomeComponent`就是一个普普通通的类,如果我们在此使用`@Module`注解,则`WelcomeComponent`类便成为了Angular的一个模块。 除了`@Component`、`@Module`注解外,我们前面还接触了将类声明为管道的注解`@Pipe`,将类型的某个属性声明为可对接父组件方法的注解`@Output()`。 ## 加入到模块 ```typescript +++ b/first-app/src/app/app.module.ts @@ -13,6 +13,7 @@ import {IndexComponent} from './index/index.component'; import { PersonalCenterComponent } from './personal-center/personal-center.component'; import { SexPipe } from './personal-center/sex.pipe'; import {XAuthTokenInterceptor} from './x-auth-token.interceptor'; +import {WelcomeComponent} from './welcome.component'; @NgModule({ @@ -23,7 +24,8 @@ import {XAuthTokenInterceptor} from './x-auth-token.interceptor'; LoginComponent, IndexComponent, PersonalCenterComponent, - SexPipe + SexPipe, + WelcomeComponent ], imports: [ BrowserModule, ``` 组件依赖于模块,没有模块的组件是没有意义的。 ## 定义selector ```typescript +++ b/first-app/src/app/welcome.component.ts @@ -3,7 +3,7 @@ import {Component} from '@angular/core'; // 使用注解来标明它是一个组件 @Component({ - + selector: 'app-welcome' }) export class WelcomeComponent { ``` 使用`selector`来定义关键字,其它的组件在V层中使用`app-welcome`来加载该组件。 ## 定义V层 V层可以定义一个单独的文件,也可以直接写在C层中: ```typescript +++ b/first-app/src/app/welcome.component.ts @@ -3,7 +3,8 @@ import {Component} from '@angular/core'; // 使用注解来标明它是一个组件 @Component({ - + selector: 'app-welcome', + template: `<h1 class="text-center">欢迎使用由软小白开发的教务管理系统</h1>` }) export class WelcomeComponent { ``` - 直接定义模板时使用`template`. - 定义模板文件时使用`templateUrl` - 可以不定义样式文件或样式 ## 测试 手动新建文件welcome.component.spec.ts ```bash panjiedeMacBook-Pro:app panjie$ pwd /Users/panjie/github/mengyunzhi/angular11-guild/first-app/src/app panjiedeMacBook-Pro:app panjie$ tree -L 1 . ├── add ├── app-routing.module.ts ├── app.component.css ├── app.component.html ├── app.component.spec.ts ├── app.component.ts ├── app.module.ts ├── edit ├── entity ├── index ├── login ├── personal-center ├── welcome.component.spec.ts 👈 ├── welcome.component.ts ├── x-auth-token.interceptor.spec.ts └── x-auth-token.interceptor.ts 6 directories, 10 files ``` 接下来,我们手动一步步的写一个测试方法,从而更清晰的明了测试文件代码的构成。 ### 增加测试方法`describe` 测试方法需要写在`describe('对测试的描述', 回调函数)`中,所以我们需要在测试文件中首先调用`describe`方法。 ```typescript +++ b/first-app/src/app/welcome.component.spec.ts @@ -0,0 +1,3 @@ +describe('welcome', () => { + +}); ``` ### 增加测试用例`fit` 执行的测试用例应写到`it('测试用例名称', 具体要执行的回调函数中)`,我们在此要测试Welcome组件是否可以初始化成功,则接着应该添加`it`方法。 ```typescript +++ b/first-app/src/app/welcome.component.spec.ts @@ -1,3 +1,5 @@ describe('welcome', () => { + fit('welcome create', () => { + }); }); ``` ### 运行测试 使用`ng t`启动项目,并查看是否运行: ![image-20210310135547484](https://img.kancloud.cn/d4/16/d416ed3960ecb2d30a569e0c7a41f2c0_662x118.png) 如上代码表明,测试用例**welcome create**成功执行,但该方法中没有任何断言。按单元测试的逻辑,我们应该在单元测试的代码以代码的形式来保证代码的正确性,在单元测试中,我们使用`expect()`关键字,比如我们断言`1+1`应该竺于`2`: ```typescript +++ b/first-app/src/app/welcome.component.spec.ts @@ -1,5 +1,5 @@ describe('welcome', () => { fit('welcome create', () => { - + expect(1 + 1).toBe(2); }); }); ``` 此时`expect`中的值与`toBe`中的值相同,则表达断言通过。表明`expect`中的代码执行结果是符合预期的。 ![image-20210310140258799](https://img.kancloud.cn/e9/b7/e9b7f7f217e9fa40dc1dc4cf5402ec72_780x274.png) ## 创建动态测试模块 在Angular的单元测试中,动态测试模块运行在一个叫`TestBed`测试机床中。它`TestBed`提供了配置动态模块的相关方法: ```typescript +++ b/first-app/src/app/welcome.component.spec.ts @@ -1,5 +1,10 @@ +import {TestBed} from '@angular/core/testing'; + describe('welcome', () => { fit('welcome create', () => { - expect(1 + 1).toBe(2); + // 配置动态测试模块 + TestBed.configureTestingModule({ + + }); }); }); ``` 预测试Welcome组件,则需要将其声明为动态测试模块的一部分: ```typescript +++ b/first-app/src/app/welcome.component.spec.ts @@ -1,10 +1,11 @@ import {TestBed} from '@angular/core/testing'; +import {WelcomeComponent} from './welcome.component'; describe('welcome', () => { fit('welcome create', () => { // 配置动态测试模块 TestBed.configureTestingModule({ - + declarations: [WelcomeComponent] }); }); }); ``` 进模块进行配置后,通过调用其`compileComponents`完成对该模块中的组件的编译(可以理解为C语言中的编译,即把一种形式转换为另一种形式)工作。 ```typescript +++ b/first-app/src/app/welcome.component.spec.ts @@ -6,6 +6,6 @@ describe('welcome', () => { // 配置动态测试模块 TestBed.configureTestingModule({ declarations: [WelcomeComponent] - }); + }).compileComponents(); }); }); ``` 随后便可以调用`TestBed`提供的 来获取一个组件实例了: ```typescript +++ b/first-app/src/app/welcome.component.spec.ts @@ -7,5 +7,8 @@ describe('welcome', () => { TestBed.configureTestingModule({ declarations: [WelcomeComponent] }).compileComponents(); + + const welcomeComponent = TestBed.createComponent(WelcomeComponent); + expect(welcomeComponent).toBeTruthy(); }); }); ``` 测试结果如下: ![image-20210310141342321](https://img.kancloud.cn/fb/c1/fbc106da4634de2176c14e60738f4960_2466x354.png) 而至于使用angular cli自动创建的测试文件为什么与我们手写的不同,待后面有机会再共同学习。 ## 完善组件 最后,我们为欢迎文字添加点颜色,并为其添加一个上边距,删除相关冗余的注释: ```typescript +++ b/first-app/src/app/welcome.component.ts @@ -4,12 +4,9 @@ import {Component} from '@angular/core'; // 使用注解来标明它是一个组件 @Component({ selector: 'app-welcome', - template: `<h1 class="text-center">欢迎使用由软小白开发的教务管理系统</h1>` + template: `<h1 class="text-center text-success mt-5 pt-5"> + 欢迎使用由软小白开发的教务管理系统</h1>` }) export class WelcomeComponent { } -// 将组件加入模块 -// 为组件设置一个selector,以使得其它组件能够使用它 -// 为组件设置V层模板 -// 新建个测试文件来测试组件 ``` ![image-20210310142042631](https://img.kancloud.cn/49/94/49948cf89689365a70cd4633b371adce_2544x1222.png) | 名称 | 地址 | | --------------- | ------------------------------------------------------------ | | Angular组件概述 | [https://angular.cn/guide/component-overview#creating-a-component-manually](https://angular.cn/guide/component-overview#creating-a-component-manually) | | TestBed | [https://angular.cn/api/core/testing/TestBed](https://angular.cn/api/core/testing/TestBed) | | 本节源码 | [https://github.com/mengyunzhi/angular11-guild/archive/step5.2.zip](https://github.com/mengyunzhi/angular11-guild/archive/step5.2.zip) |