目录
- Angular 导学
- Angular CLI以及工程结构
- Angular componets(组件概览,组件交互,组件生命周期,内容投影)
- Angular Module
- Angular模板(文本插值,属性绑定,类/样式绑定,事件绑定,双向绑定,模板引用变量)
- Angular路由与导航
- Angular指令(常用内置指令,属性指令,结构指令)
- Angular依赖注入
- Angular http模块
- Angular从组件生命周期深入到脏值检测(与Vue等其他框架的数据检测更新方案的对比)
- Angular表单
TimeLine
| 内容 | 负责人 | 预计时间 |
|---|---|---|
| 1~3课时 | max,zhang | 7.23 |
| 4~5课时 | sun,David | 7.30 |
| 6~7课时 | tiny,Liu | 8.9 |
| 8~9课时 | tiny,Liu | 8.16 |
| 10课时 | max,zhang | 8.23 |
| 11课时 | sun,David | 8.31 |
Angular 导学
前言
这次系列课程会给大家带来angular9基础知识点+实例模拟的内容,
讲解中所有的例子都可以在https://cnnkgapt12.cn.kworld.kpmg.com/mzhang38/my-angular-app中找到,每次课程内容在对应的分支当中。
开始之前请安装angular CLI
npm install -g @angular/cli
Angular优势
框架层面
react/vue:更加针对于视图View层的轻量级类库
Angular: 更能称之为一个框架,模块,组件,服务….等全部都给你提供好了,开箱即用,也就是说你不需要依赖很多三方的开源的类库,不需要去比较这些三方类库的优缺点。
语言层面
react/vue: 都在拥抱TypeScript,TypeScript已经成为前端的标配,包括VUE3.0 已经使用TS开发,大势所趋
angular:是所有主流框架最早拥抱TS,并且采用TS作为开发语言,提供了大型项目所必须的类型约束。虽然目前react,Vue 都支持TS,但是从集成程度上来看,angular是最友好和最方便的。
生态层面
angular背后两大巨头,谷歌和微软,谷歌很好理解,因为angular就是谷歌团队开发的嘛。然而angular有两大底层建筑是微软提供的,TypeScript 和 Rxjs 。背靠两大巨头,生态基础非常可靠。
项目层面
- 大型项目非常复杂的逻辑,处理这些逻辑大多数都是异步操作,然而angular 深度集成了Rxjs,用Rxjs来处理对于逻辑和异步的响应式编程,这种处理使得对于逻辑和异步的处理非常自然简单。
- 项目中需要的复杂的表单处理,angular提供了很好的机制让你处理这些复杂表单的约束以及验证
- 很好的文件结构和编码规范,这方面angular都提供好了

Angular CLI以及工程结构
CLI 构建器用来快速构建angular应用
- 要想创建、构建或在开发服务器上运行一个新的、基本的 Angular 项目,请到这个新工作区的上级目录中运行下列命令:
ng new my-angular-appcd my-angular-appng serve
工程结构
- workspace configuration files

| 工作区配置文件 | 用途 |
|---|---|
| .editorconfig | 代码编辑器的配置文件 |
| .gitignore | 指定 Git 应忽略的不必追踪的文件。 |
| README.md | 根应用的简介文档. |
| angular.json | CLI 要用到的构建、启动开发服务器和测试工具的配置项。 |
| package.json | 配置工作区中所有项目可用的 npm 包依赖。 |
| package-lock.json | 提供 npm 客户端安装到 node_modules 的所有软件包的版本信息。 |
| src/ | 根项目的源文件。 |
| node_modules/ | 为整个工作区提供 npm 包 |
| tsconfig.json | 工作区中所有项目的基本 TypeScript 配置。所有其它配置文件都继承自这个基本配置。 |
| tslint.json | 工作区中所有项目的默认的 TSLint 配置。 |
- application source file

| 应用支持文件 | 目的 |
|---|---|
| app/ | 包含定义应用逻辑和数据的组件文件 |
| assets/ | 静态资源文件。 |
| environments/ | 包含特定目标环境的构建配置选项。默认情况下,有一个无名的标准开发环境和一个生产(“prod”)环境。你还可以定义其它的目标环境配置。 |
| favicon.ico | 用作该应用在标签栏中的图标。 |
| index.html | 当有人访问你的站点时,提供服务的主要 HTML 页面。CLI 会在构建你的应用时自动添加所有的 JavaScript 和 CSS 文件 |
| main.ts | 应用的主要切入点 |
| polyfills.ts | 为浏览器支持提供了polyfill脚本。 |
| styles.less | 列出为项目提供样式的 CSS 文件。该扩展还反映了你为该项目配置的样式预处理器。 |
| test.ts | 单元测试的主入口点,带有一些 Angular 特有的配置。你通常不需要编辑这个文件。 |
Angular Components
- 组件长什么样子?
- 如何创建组件?
- 组件生命周期?
- 组件之间的数据交互方式?
- 怎么实现组件投影?
组件长什么样子?
- 文件结构

- html&css文件大家都比较熟悉了,我们着重看一下xxxx.component.ts文件结构,以app.compontent.ts 为例
import { Component } from '@angular/core';@Component({selector: 'app-root', //这个 CSS 选择器用于在模板中标记出该指令,并触发该指令的实例化templateUrl: './app.component.html',//Angular 组件模板文件的 URLstyleUrls: ['./app.component.less']//一个或多个 URL,指向包含本组件 CSS 样式表的文件})export class AppComponent {title = 'my-angular-app';}
- @Compont:这是一个组件装饰器,用于把某个类标记为 Angular 组件,并为它配置一些元数据,以决定该组件在运行期间该如何处理、实例化和使用。类似于Java里的注解,angular有很多的装饰器,他将元数据添加到类、类成员(属性、方法)和函数参数上。(要了解更多可以去看下TypeScript装饰器原理),以上是常用元数据属性,还有很多属性类似
changeDetection/animations可以自行翻阅官网API
如何创建组件?
- 老生常谈的内容直接看demo1(https://cnnkgapt12.cn.kworld.kpmg.com/mzhang38/my-angular-app/tree/SESSION1/src/app/lifecycle)

可知常用钩子执行顺序如上
- 执行demo2(https://cnnkgapt12.cn.kworld.kpmg.com/mzhang38/my-angular-app/tree/SESSION1/src/app/onChange)

可知:
| 钩子方法 | 用途 | 时机 |
|---|---|---|
ngOnChanges() |
当组件设置或重新设置数据绑定的输入属性时响应。 该方法接受当前和上一属性值的 [SimpleChanges](https://angular.cn/api/core/SimpleChanges) 对象注意,这发生的非常频繁,所以你在这里执行的任何操作都会显著影响性能。 |
在 ngOnInit() 之前以及所绑定的一个或多个输入属性的值发生变化时都会调用。注意,如果你的组件没有输入,或者你使用它时没有提供任何输入,那么框架就不会调用 ngOnChanges()。 |
ngOnInit() |
在组件第一次显示数据绑定和设置指令/组件的输入属性之后,初始化指令/组件 | 在第一轮 ngOnChanges() 完成之后调用,只调用一次。 |
ngDoCheck() |
发生脏值检测时会调用的钩子 | 紧跟在每次执行变更检测时的 ngOnChanges() 和 首次执行变更检测时的 ngOnInit() 后调用。 |
- 浅谈Dngular变更检测
- 变更检测的作用—— 如果组件属性被改变,检测组件模版上的所有绑定关系,与其绑定的模版相应区域可能需要更新的过程。类似于VUE里的数据劫持,react中的Fiber
- 何时执行——异步事件(用户输入操作,比如点击,提交等,请求服务端数据,定时事件等可能改变绑定属性的操作)
- ngOnchange()和ngDocheck()有何不同
- demo的多次点击change按钮可知,一旦执行变更检测必定会调用ngDoCheck(),而ngOnchange()是执行变更检测后属性值发生变化时候才会调用
- ngOnchange()无法检测到应用类型值的变化,而ngDoCheck()可以(见https://cnnkgapt12.cn.kworld.kpmg.com/mzhang38/my-angular-app/tree/SESSION1/src/app/lifecycle点击change list)
ngAfterContentInit() |
当 Angular 把外部内容投影进组件视图或指令所在的视图之后调用。 | 第一次 ngDoCheck() 之后调用,只调用一次。 |
|---|---|---|
ngAfterContentChecked() |
每当 Angular 检查完被投影到组件或指令中的内容之后调用。 | ngAfterContentInit() 和每次 ngDoCheck() 之后调用 |
ngAfterViewInit() |
当 Angular 初始化完组件视图及其子视图或包含该指令的视图之后调用。 | 第一次 ngAfterContentChecked() 之后调用,只调用一次。 |
ngAfterViewChecked() |
每当 Angular 做完组件视图和子视图或包含该指令的视图的变更检测之后调用。 | ngAfterViewInit() 和每次 ngAfterContentChecked() 之后调用。 |
ngOnDestroy() |
每当 Angular 每次销毁指令/组件之前调用并清扫。 在这儿反订阅可观察对象和分离事件处理器,以防内存泄漏。 | 在 Angular 销毁指令或组件之前立即调用。 |
- 带check的生命周期和doCheck类似都是变更检测时执行
- ngAfterContentInit()和ngAfterViewInit() 需要注意需要操作组件视图实例时候,需要在此或之后获取。
组件间的数据交互
- 通过输入性属性把数据从父组件传递给子组件,@Input()装饰器
- 通过setter截听输入属性的变化
- 父组件监听子组件的事件(demo中父组件如何获取子组件输入的变化??)
- 子组件暴露一个
[EventEmitter](https://angular.cn/api/core/EventEmitter)属性,当事件发生时,子组件利用该属性emits(向上发布)事件。父组件绑定到这个事件属性,并在事件发生时作出回应。 - 子组件的
[EventEmitter](https://angular.cn/api/core/EventEmitter)属性是一个输出属性,通常带有@Output 装饰器 ```typescript import {Component, Input, Output, EventEmitter} from ‘@angular/core’;
- 子组件暴露一个
@Component({ selector: ‘setter-demo’, templateUrl: ‘./setter.component.html’ }) export class SetterComponent { /**
* @Input()装饰器定义输入属性,通过输入性属性把数据从父组件传递给子组件* */@Input()/*** 通过setter截听输入属性的变化* */set name(name: string) {this._name = name.toUpperCase();}get name(): string {return this._name;}private _name = '';/*** @Output()装饰器暴露一个EventEmitter属性,父组件绑定此事件属性定义回应方法* */@Output() inputChange = new EventEmitter<string>();constructor() {}change($event: string) {console.log($event)/*** @子组件利用inputChange.emit(向上发布)事件* *///this.inputChange.emit(this.name)}
}
4. 封装一个input类型组件,如何实现双向数据绑定```typescriptimport {Component, Input, Output, EventEmitter, OnInit} from '@angular/core';@Component({selector: 'my-input-demo',templateUrl: './my-input.component.html'})export class MyInputComponent implements OnInit {@Input() value = ''/*** angular中约定@input()属性名+change 定义@output EventEmitter属性名,即可在调用时实现[(value)]的双向绑定写法* */@Output() valueChange = new EventEmitter<string>();options: any[] = []change($event: string) {this.valueChange.emit($event)}ngOnInit() {const that = thissetTimeout(() => {this.options = [{id: '1', name: 'Max'},{id: '2', name: 'Tiny'},{id: '3', name: 'David'},]}, 100)}}
- 父组件通过@ViewChild获取子组件访问权
- 通过依赖注入service共享数据(后面章节讲依赖注入时候会讲解) ```typescript import {Component, ViewChild} from ‘@angular/core’; import {ViewChildComponent} from “../viewChild/view-child.component”;
@Component({ selector: ‘main-demo’, templateUrl: ‘./main.component.html’, styleUrls: [‘./main.component.less’] }) export class MainComponent { title = ‘my-angular-app’; name = ‘kpmg’
/**父组件通过@ViewChild获取子组件访问权** 任何带有 @Component 或 @Directive 装饰器的类** 字符串形式的模板引用变量(比如可以使用 @ViewChild('cmp') 来查询 <my-component #cmp></my-component>** 组件树中任何当前组件的子组件所定义的提供者(比如 @ViewChild(SomeService) someService: SomeService )** TemplateRef(比如可以用 @ViewChild(TemplateRef) template; 来查询 <ng-template></ng-template>)* */@ViewChild(ViewChildComponent)private childComponent!: ViewChildComponent;//非空断言constructor() {}inputChange($event: string) {this.name = $event}start() {this.childComponent.start();}stop() {this.childComponent.stop();}
}
<a name="NXIrS"></a>#### 组件内容投影1. 使用<ng-content>实现内容映射1. `ngTemplateOutlet` 指令来渲染给定的 `ng-template` 元素,ng-template是Angular 结构型指令中的一种,用于定义模板。定义的模板不会直接显示出来,需要通过其他结构型指令(如 ng-if)将模块内容渲染到页面中。1. <ng-content>和<ng-template> 区别:`ng-template` 元素,你可以让组件根据你想要的任何条件显式渲染内容,并可以进行多次渲染。在显式渲染 `ng-template` 元素之前,Angular 不会初始化该元素的内容。```typescript<div><div class="header" *ngIf="template;else header"><!--ngTemplateOutlet 指令来渲染给定的 传入ng-template 元素--><ng-container *ngTemplateOutlet="template"></ng-container></div><ng-template #header><div class="header">默认的header</div></ng-template><div class="content"><label>content</label><!--使用ng-content 接收映射内容--><ng-content></ng-content></div><div class="footer"><label>footer</label><!--使用ng-content select 属性实现多插槽内容投影--><ng-content select="[footer]"></ng-content></div></div>import {Component, Input, TemplateRef} from '@angular/core';@Component({selector: 'template-outlet-demo',templateUrl: './template-outlet.component.html',styleUrls: ['./template-outlet.component.less']})export class TemplateOutletComponent {/*** 传入谁属性不只是string,number等常用类型,也可以是一个TemplateRef 类型* */@Input() template!: TemplateRef<void>;}<h1>内容投影 demo</h1><template-outlet-demo><my-input-demo [(value)]="user"></my-input-demo></template-outlet-demo><br><br><br><br><br><br><br><br><h1>多个内容投影 demo</h1><template-outlet-demo><my-input-demo [(value)]="user"></my-input-demo><div footer>footer 投影内容</div></template-outlet-demo><br><br><br><br><br><br><br><br><h1>ngTemplateOutlet demo</h1><template-outlet-demo [template]="header"></template-outlet-demo><ng-template #header><a>这里是自定义header</a></ng-template>
???实现一个下面的input 组件 ,右部可以自定义传入自定义的模板或者文字。
