国足虐我千百次 發表於 2022-12-7 18:31:00

Angular新手入门指南【最全】

<p>&nbsp;</p>
<h2><strong>Angular概述</strong></h2>
<p>Angular 是谷歌开发的一款开源的 web 前端框架,基于 TypeScript 。</p>
<p>和 react 与 vue 相比, Angular 更适合中大型企业级项目。</p>
<h3><strong>Angular程序架构</strong></h3>
<p><img src="https://img.php.cn/upload/article/000/000/024/6157a13ebf7a44ebb42b81812e0528d3-0.png"></p>
<h3><strong>Angular优势</strong></h3>
<ul>
<li>可伸缩性:基于RxJS 、immutable.js和其他推送模型,能适应海量数据需求</li>
<li>跨平台:渐进式应用(高性能、离线使用、免安装),原生(Ionic),桌面端</li>
<li>生产率:模版(通过简单而强大的模版语法,快速创建UI视图),CLI(快速进入构建环节、添加组件和测试,然后立即部署)</li>
<li>测试:单元测试(支持Karma、Jasmine等工具进行单元测试),端到端测试(支持Protractor等工具进行端到端测试)【相关教程推荐:《angular教程》】</li>
</ul>
<h3><strong>@angular/cli脚手架</strong></h3>
<p>ng new 新建项目</p>
<ul>
<li>——routing 配置路由</li>
<li>——style=css|scss|less 配置css样式</li>
</ul>
<p>ng serve 启动项目</p>
<ul>
<li>——port 4200 端口号,默认4200</li>
<li>——open 自动打开浏览器</li>
</ul>
<p>ng build 打包项目</p>
<ul>
<li>——aot 预编译</li>
<li>——prod 压缩打包</li>
<li>——base-href=/static/</li>
</ul>
<p>ng generate 创建模块/组件/服务</p>
<ul>
<li>module ——routing 创建模块</li>
<li>component 创建组件</li>
<li>service / 创建服务</li>
</ul>
<h3>文件加载顺序</h3>
<blockquote>
<p>main.ts =&gt; app.module.ts =&gt; app.component.ts =&gt; index.html =&gt; app.component.html</p>
</blockquote>
<h3>项目目录结构</h3>
<div class="cnblogs_Highlighter">
<pre class="brush:javascript;gutter:true;">|-- project
        |-- .editorconfig // 用于在不同编辑器中统一代码风格
        |-- .gitignore // git中的忽略文件列表
        |-- README.md // markdown格式的说明文件
        |-- angular.json // angular的配置文件
        |-- browserslist // 用于配置浏览器兼容性的文件
        |-- karma.conf.js // 自动化测试框架Karma的配置文件
        |-- package-lock.json // 依赖包版本锁定文件
        |-- package.json // npm的包定义文件
        |-- tsconfig.app.json // 用于app项目的ts配置文件
        |-- tsconfig.json // 整个工作区的ts配置文件
        |-- tsconfig.spec.json // 用于测试的ts配置文件
        |-- tslint.json // ts的代码静态扫描配置
        |-- e2e // 自动化集成测试目录
        |-- src // 源代码目录

|-- src // 源代码目录
        |-- favicon.ico // 收藏图标
        |-- index.html // 单页应用到宿主HTML
        |-- main.ts // 入口 ts 文件
        |-- polyfills.ts // 用于不同浏览器的兼容脚本加载
        |-- styles.css // 整个项目的全局css
        |-- test.ts // 测试入口
        |-- app // 工程源码目录
        |-- assets // 资源目录
        |-- environments // 环境配置
                |-- environments.prod.ts // 生产环境
                |-- environments.ts // 开发环境</pre>
</div>
<h2><strong>Angular模块</strong></h2>
<p>在 app.module.ts 中定义 AppModule,这个根模块会告诉 Angular 如何组装应用。</p>
<p><img src="https://img.php.cn/upload/article/000/000/024/a085f0538ff33d0bd5aae50bdbeeeef2-1.png"></p>
<h3><strong>@NgModule 装饰器</strong></h3>
<p>@NgModule 接受一个元数据对象,告诉 Angular 如何编译和启动应用</p>
<p><strong>设计意图</strong></p>
<ul>
<li>静态的元数据(declarations)</li>
<li>运行时的元数据(providers)</li>
<li>组合与分组(imports 和 exports)</li>
</ul>
<p><strong>元数据</strong></p>
<ul>
<li>declarations 数组:模块拥有的组件、指令或管道,注意每个组件/指令/管道只能在一个模块中声明</li>
<li>providers 数组: 模块中需要使用的服务</li>
<li>imports 数组:导入本模块需要的依赖模块,注意是模块</li>
<li>exports 数组: 暴露给其他模块使用的组件、指令或管道等</li>
<li>bootstrap 数组:指定应用的主视图(称为根组件)通过引导根 AppModule 来启动应用,即项目刚加载时选择读哪个组件</li>
<li>entryComponents 数组:一般用于动态组件</li>
</ul>
<h3><strong>内置模块</strong></h3>
<p>常用的有:核心模块、通用模块、表单模块、网络模块等</p>
<p><img src="https://img.php.cn/upload/article/000/000/024/acd6a53d6b1a5e051dd43726899638bb-2.png"></p>
<h3><strong>自定义模块</strong></h3>
<p>当项目比较小的时候可以不用自定义模块</p>
<p>但是当项目非常庞大的时候,把所有的组件都挂载到根模块里面就不太合适了</p>
<p>所以可以使用自定义模块来组织项目,并且通过自定义模块可以实现路由的懒加载</p>
<h3><strong>模块的tips</strong></h3>
<p>导入其他模块时,需要知道使用该模块的目的</p>
<ul>
<li>如果是组件,那么需要在每一个需要的模块中都进行导入</li>
<li>如果是服务,那么一般来说在根模块导入一次即可</li>
</ul>
<p>需要在每个需要的模块中进行导入的</p>
<ul>
<li><code>CommonModule</code>&nbsp;: 提供绑定、*ngIf 和 *ngFor 等基础指令,基本上每个模块都需要导入它</li>
<li><code>FormsModule / ReactiveFormsModule</code>&nbsp;: 表单模块需要在每个需要的模块导入</li>
<li>提供组件、指令或管道的模块</li>
</ul>
<p>只在根模块导入一次的</p>
<ul>
<li><code>HttpClientModule / BrowerAnimationsModule NoopAnimationsModule</code></li>
<li>只提供服务的模块</li>
</ul>
<h2><strong>Angular组件</strong></h2>
<p><img src="https://img.php.cn/upload/article/000/000/024/c9285356a883dd08ee52c03fa66164b3-3.png"></p>
<ul>
<li>组件是 Angular 的核心,是 Angular 应用中最基本的 UI 构造块,控制屏幕上被称为视图的一小片区域</li>
<li>组件必须从属于某个&nbsp;<code>NgModule</code>&nbsp;才能被其他组件或应用使用</li>
<li>组件在&nbsp;<code>@NgModule</code>&nbsp;元数据的&nbsp;<code>declarations</code>&nbsp;字段中引用</li>
</ul>
<h3><strong>@Component 元数据</strong></h3>
<ul>
<li>selector :选择器,选择相匹配的HTML里的指令模版</li>
<li>templateUrl :将选择器中匹配的指令同级替换成值的模版</li>
<li>template :内嵌模版,直接可以在里面写HTML模版</li>
<li>styleUrls :对应模版的样式,为一个数组,可以引入多个css样式控制组件</li>
<li>encapsulation:组件样式封装策略</li>
</ul>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 0, 1)">@Component({
selector: </span>'app-xxx'<span style="color: rgba(0, 0, 0, 1)">,
templateUrl: </span>'XXX'<span style="color: rgba(0, 0, 0, 1)">,
styleUrls: [</span>'XXX'<span style="color: rgba(0, 0, 0, 1)">],
encapsulation:ViewEncapsulation.Emulated</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 不写则默认该值,表示该组件样式只作用于组件本身,不影响全局样式,在 head 中生成单独的 style 标签</span>
})</pre>
</div>
<h3><strong>数据绑定</strong></h3>
<ul>
<li>
<p>数据绑定&nbsp;<code>{{data}}</code></p>
</li>
<li>
<p>属性绑定&nbsp;<code>="id"</code>,其中<code>=“判断表达式”</code>是在应用单个<code>class</code>样式时的常用技巧</p>
</li>
<li>
<p>事件绑定&nbsp;<code>(keyup)="keyUpFn($event)"</code></p>
</li>
<li>
<p>样式绑定可以用&nbsp;<code>:host</code>&nbsp;这样一个伪类选择器,绑定的样式作用于组件本身</p>
</li>
<li>
<p>双向数据绑定&nbsp;<code>[(ngModel)]</code></p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 注意引入:FormsModule</span>
import { FormsModule } from '@angular/forms'<span style="color: rgba(0, 0, 0, 1)">;

</span>&lt;input type="text" [(ngModel)]="inputValue"/&gt; {{inputValue}}

<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 其实是一个语法糖</span>
="username" (ngModelChange)="username = $event"</pre>
</div>
<p>&nbsp;</p>
</li>
</ul>
<h3><strong>脏值检测</strong></h3>
<p>脏值检测:当数据改变时更新视图(DOM)</p>
<p>如何进行检测:检测两个状态值(当前状态和新状态)</p>
<p>何时触发脏值检测:浏览器事件(<code>click</code>、<code>mouseover</code>、<code>keyup</code>等)、<code>setTimeout()</code>或<code>setInterval()</code>、HTTP请求</p>
<p>Angular 有两种变更检测策略:<code>Default</code>&nbsp;和&nbsp;<code>OnPush</code></p>
<p>可以通过在<code>@Component</code>元数据中设置<code>changeDetection: ChangeDetectionStrategy.OnPush</code>进行切换</p>
<p><code>Default</code>:</p>
<p>优点:每一次有异步事件发生,Angular 都会触发变更检测,从根组件开始遍历其子组件,对每一个组件都进行变更检测,对dom进行更新。</p>
<p>缺点:有很多组件状态没有发生变化,无需进行变更检测。如果应用程序中组件越多,性能问题会越来越明显。</p>
<p><code>OnPush</code>:</p>
<p>优点:组件的变更检测完全依赖于组件的输入(<code>@Input</code>),只要输入值不变就不会触发变更检测,也不会对其子组件进行变更检测,在组件很多的时候会有明显的性能提升。</p>
<p>缺点:必须保证输入(<code>@Input</code>)是不可变的(可以用<code>Immutable.js</code>解决),每一次输入变化都必须是新的引用。</p>
<h3><strong>父子组件通讯</strong></h3>
<p><img src="https://img.php.cn/upload/article/000/000/024/6d0f15a4f39080847d564462dbd64019-4.png"></p>
<p><strong>父组件给子组件传值 @input</strong></p>
<p>父组件不仅可以给子组件传递简单的数据,还可把自己的方法以及整个父组件传给子组件。</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 父组件调用子组件的时候传入数据</span>
&lt;app-header ="msg"&gt;&lt;/app-header&gt;

<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 子组件引入 Input 模块</span>
import { Component, OnInit ,Input } from '@angular/core'<span style="color: rgba(0, 0, 0, 1)">;

</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 子组件中 @Input 装饰器接收父组件传过来的数据</span>
<span style="color: rgba(0, 0, 0, 1)">export class HeaderComponent implements OnInit {
@Input() msg:string
    constructor() { }
    ngOnInit() { }
}

</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 子组件中使用父组件的数据</span>
&lt;h2&gt;这是头部组件--{{msg}}&lt;/h2&gt;</pre>
</div>
<p>&nbsp;</p>
<p>**子组件触发父组件的方法 @Output **</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 子组件引入 Output 和 EventEmitter</span>
import { Component,OnInit,Input,Output,EventEmitter} from '@angular/core'<span style="color: rgba(0, 0, 0, 1)">;

</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 子组件中实例化 EventEmitter</span><span style="color: rgba(0, 128, 0, 1)">
//</span><span style="color: rgba(0, 128, 0, 1)"> 用 EventEmitter 和 @Output 装饰器配合使用 &lt;string&gt; 指定类型变量</span>
@Output() private outer=<span style="color: rgba(0, 0, 255, 1)">new</span> EventEmitter&lt;string&gt;<span style="color: rgba(0, 0, 0, 1)">();

</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 子组件通过 EventEmitter 对象 outer 实例广播数据</span>
<span style="color: rgba(0, 0, 0, 1)">sendParent(){
</span><span style="color: rgba(0, 0, 255, 1)">this</span>.outer.emit('msg from child'<span style="color: rgba(0, 0, 0, 1)">)
}

</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 父组件调用子组件的时候,定义接收事件,outer 就是子组件的 EventEmitter 对象 outer</span>
&lt;app-header (outer)="runParent($event)"&gt;&lt;/app-header&gt;

<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 父组件接收到数据会调用自己的 runParent, 这个时候就能拿到子组件的数据</span><span style="color: rgba(0, 128, 0, 1)">
//</span><span style="color: rgba(0, 128, 0, 1)"> 接收子组件传递过来的数据</span>
<span style="color: rgba(0, 0, 0, 1)">runParent(msg:string){
   alert(msg);
}</span></pre>
</div>
<p>&nbsp;</p>
<p><strong>父组件通过 ViewChild 主动调用子组件DOM和方法</strong></p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 给子组件定义一个名称</span>
&lt;app-footer #footerChild&gt;&lt;/app-footer&gt;

<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 引入 ViewChild</span>
import { Component, OnInit ,ViewChild} from '@angular/core'<span style="color: rgba(0, 0, 0, 1)">;

</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> ViewChild 和子组件关联起来</span>
@ViewChild('footerChild'<span style="color: rgba(0, 0, 0, 1)">) footer;

</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 调用子组件</span>
<span style="color: rgba(0, 0, 0, 1)">run(){
   </span><span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.footer.footerRun();
}</span></pre>
</div>
<p>&nbsp;</p>
<h3>投影组件</h3>
<p><img src="https://img.php.cn/upload/article/000/000/024/c50f52d15d08b5fe75de151cadf784df-5.png"></p>
<blockquote>
<p>由于组件过度嵌套会导致数据冗余和事件传递,因此引入投影组件的概念</p>
</blockquote>
<p>投影组件&nbsp;<code>ng-content</code>&nbsp;作为一个容器组件使用</p>
<p>主要用于组件动态内容的渲染,而这些内容没有复杂的业务逻辑,也不需要重用,只是一小部分 HTML 片段</p>
<p>使用&nbsp;<code>ng-content</code>&nbsp;指令将父组件模板中的任意片段投影到它的子组件上</p>
<p>组件里面的&nbsp;<code>ng-content</code>&nbsp;部分可以被组件外部包裹的元素替代</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 表现形式: &lt;ng-content select="样式类/HTML标签/指令"&gt;&lt;/ng-content&gt;</span>

&lt;ng-content select=""&gt;&lt;/ng-content&gt;</pre>
</div>
<p><code>select</code>&nbsp;表明包含&nbsp;<code>appGridItem</code>&nbsp;的指令的元素才能投影穿透过来</p>
<h2><strong>Angular指令</strong></h2>
<p><img src="https://img.php.cn/upload/article/000/000/024/c50f52d15d08b5fe75de151cadf784df-6.png"></p>
<blockquote>
<p>指令可以理解为没有模版的组件,它需要一个宿主元素(Host)</p>
<p>推荐使用方括号 [] 指定 Selector,使它变成一个属性</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 0, 1)">@Directive({
selector: </span>''<span style="color: rgba(0, 0, 0, 1)">
})</span></pre>
</div>
</blockquote>
<h3><strong>内置属性型指令</strong></h3>
<p><strong>NgClass</strong></p>
<p><code>ngClass</code>&nbsp;是自由度和拓展性最强的样式绑定方式</p>
<div class="cnblogs_code">
<pre>&lt;div ="{'red': true, 'blue': false}"&gt;<span style="color: rgba(0, 0, 0, 1)">
这是一个 div
</span>&lt;/div&gt;</pre>
</div>
<p><strong>NgStyle</strong></p>
<p><code>ngStyle</code>由于是嵌入式样式,因此可能会覆盖掉其他样式,需谨慎</p>
<div class="cnblogs_code">
<pre>&lt;div ="{'background-color':'green'}"&gt;你好 ngStyle&lt;/div&gt;</pre>
</div>
<p><strong>NgModel</strong></p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 注意引入:FormsModule</span>
import { FormsModule } from '@angular/forms'<span style="color: rgba(0, 0, 0, 1)">;

</span>&lt;input type="text" [(ngModel)]="inputValue"/&gt; {{inputValue}}</pre>
</div>
<h3><strong>内置结构型指令</strong></h3>
<p><strong>ngIf</strong></p>
<p>ngIf 根据表达式是否成立,决定是否展示 DOM 标签</p>
<div class="cnblogs_Highlighter">
<pre class="brush:javascript;gutter:true;">&lt;p *ngIf="list.length &gt; 3"&gt;这是 ngIF 判断是否显示&lt;/p&gt;</pre>
</div>
<p><strong>ngIf else</strong></p>
<div class="cnblogs_code">
<pre>&lt;div *ngIf="show else ElseContent"&gt;这是 ngIF 内容&lt;/div&gt;
&lt;ng-template #ElseContent&gt;
&lt;h2&gt;这是 <span style="color: rgba(0, 0, 255, 1)">else</span> 内容&lt;/h2&gt;
&lt;/ng-template&gt;

<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 结构性指令都依赖于 ng-template,*ngIf 实际上就是 ng-template 指令的 属性。</span></pre>
</div>
<p><strong>ngFor</strong></p>
<div class="cnblogs_code">
<pre>&lt;ul&gt;
&lt;li *ngFor="let item of list;let i = index;"&gt;<span style="color: rgba(0, 0, 0, 1)">
   {{item}} </span>--<span style="color: rgba(0, 0, 0, 1)">{{i}}
</span>&lt;/li&gt;
&lt;/ul&gt;</pre>
</div>
<p><strong>ngSwitch</strong></p>
<div class="cnblogs_code">
<pre>&lt;ul ="score"&gt;
   &lt;li *ngSwitchCase="1"&gt;已支付&lt;/li&gt;
   &lt;li *ngSwitchCase="2"&gt;已确认&lt;/li&gt;
   &lt;li *ngSwitchCase="3"&gt;已发货&lt;/li&gt;
   &lt;li *ngSwitchDefault&gt;已失效&lt;/li&gt;
&lt;/ul&gt;</pre>
</div>
<h3><strong>指令事件样式绑定</strong></h3>
<p><code>@HostBinding</code>&nbsp;绑定宿主的属性或者样式</p>
<div class="cnblogs_code">
<pre>@HostBinding('style.display') display = "grid"<span style="color: rgba(0, 0, 0, 1)">;

</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 用样式绑定代替rd2的 this.setStyle('display','grid');</span></pre>
</div>
<p><code>@HostListener</code>&nbsp;绑定宿主的事件</p>
<div class="cnblogs_code">
<pre>@HostListener('click',['$event.target'<span style="color: rgba(0, 0, 0, 1)">])

</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 第一个参数是事件名,第二个是事件携带参数</span></pre>
</div>
<h2><strong>Angular生命周期</strong></h2>
<p>生命周期函数通俗的讲就是组件创建、组件更新、组件销毁的时候会触发的一系列的方法</p>
<p>当 Angular 使用构造函数新建一个组件或指令后,就会按下面规定的顺序在特定时刻调用生命周期钩子</p>
<blockquote>
<ul>
<li>
<p>constructor :构造函数永远首先被调用,一般用于变量初始化以及类实例化</p>
</li>
<li>
<p>ngOnChanges :被绑定的输入属性变化时被调用,首次调用一定在 ngOnInit 之前。输入属性发生变化是触发,但组件内部改变输入属性是不会触发的。注意:如果组件没有输入,或者使用它时没有提供任何输入,那么框架就不会调用 ngOnChanges</p>
</li>
<li>
<p>ngOnInit :组件初始化时被调用,在第一轮 ngOnChanges 完成之后调用,只调用一次。使用 ngOnInit 可以在构造函数之后马上执行复杂的初始化逻辑,同时在 Angular 设置完输入属性之后,可以很安全的对该组件进行构建</p>
</li>
<li>
<p>ngDoCheck :脏值检测时调用,在变更检测周期中 ngOnChanges 和 ngOnInit 之后</p>
<ul>
<li>
<p>ngAfterContentInit :内容投影ng-content完成时调用,只在第一次 ngDoCheck 之后调用</p>
</li>
<li>
<p>ngAfterContentChecked: 每次完成被投影组件内容的变更检测之后调用(多次)</p>
</li>
<li>
<p>ngAfterViewInit :组件视图及子视图初始化完成时调用,只在第一次 ngAfterContentChecked 调用一次</p>
</li>
<li>
<p>ngAfterViewChecked: 检测组件视图及子视图变化之后调用(多次)</p>
</li>
</ul>
</li>
<li>
<p>ngOnDestroy 当组件销毁时调用,可以反订阅可观察对象和分离事件处理器,以防内存泄漏</p>
</li>
</ul>
</blockquote>
<h2><strong>Angular路由</strong></h2>
<blockquote>
<p>路由(导航)本质上是切换视图的一种机制,路由的导航URL并不真实存在</p>
<p>Angular 的路由借鉴了浏览器URL变化导致页面切换的机制</p>
<p>Angular 是单页程序,路由显示的路径不过是一种保存路由状态的机制,这个路径在 web 服务器上不存在</p>
</blockquote>
<h3><strong>路由基本配置</strong></h3>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 0, 1)">/*</span><span style="color: rgba(0, 128, 0, 1)">*
* 在功能模块中定义子路由后,只要导入该模块,等同于在根路由中直接定义
* 也就是说在 AppModule 中导入 HomeModule 的时候,
* 由于 HomeModule 中导入了 HomeRouting Module
* 在 HomeRoutingModule 中定义的路由会合并到根路由表
* 相当于直接在根模块中定义下面的数组。
* const routes = [{
*   path: 'home',
*   component: HomeContainerComponent
* }]
</span><span style="color: rgba(0, 128, 0, 1)">*/</span><span style="color: rgba(0, 0, 0, 1)">

const routes: Routes </span>=<span style="color: rgba(0, 0, 0, 1)"> [
{path: </span>'home'<span style="color: rgba(0, 0, 0, 1)">, component: HomeComponent},
{path: </span>'news'<span style="color: rgba(0, 0, 0, 1)">, component: NewsComponent},
{path: </span>'newscontent/:id', component: NewscontentComponent},<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 配置动态路由</span>
<span style="color: rgba(0, 0, 0, 1)">{
    path: </span>''<span style="color: rgba(0, 0, 0, 1)">,
    redirectTo: </span>'/home',<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 重定向</span>
    pathMatch: 'full'<span style="color: rgba(0, 0, 0, 1)">
    },
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">匹配不到路由的时候加载的组件 或者跳转的路由</span>
<span style="color: rgba(0, 0, 0, 1)">{
   path: </span>'**', <span style="color: rgba(0, 128, 0, 1)">/*</span><span style="color: rgba(0, 128, 0, 1)">任意的路由</span><span style="color: rgba(0, 128, 0, 1)">*/</span>
   <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> component:HomeComponent</span>
   redirectTo:'home'<span style="color: rgba(0, 0, 0, 1)">
}
]

@NgModule({
</span><span style="color: rgba(0, 128, 0, 1)">/*</span><span style="color: rgba(0, 128, 0, 1)">*
   * 根路由使用 `RouterModule.forRoot(routes)` 形式。
   * 而功能模块中的路由模块使用 `outerModule.forChild(routes)` 形式。
   * 启用路由的 debug 跟踪模式,需要在根模块中设置 `enableTracing: true`
   </span><span style="color: rgba(0, 128, 0, 1)">*/</span><span style="color: rgba(0, 0, 0, 1)">
imports: ,
exports:
})
export class AppRoutingModule { }</span></pre>
</div>
<h3><strong>激活路由</strong></h3>
<p>找到&nbsp;<code>app.component.html</code>&nbsp;根组件模板,配置&nbsp;<code>router-outlet</code></p>
<p>通过模版属性访问路由,即路由链接&nbsp;<code>routerLink</code></p>
<div class="cnblogs_code">
<pre>&lt;h1&gt;
&lt;a ="['/home']"&gt;首页&lt;/a&gt;
&lt;a ="['/home',tab.link]"&gt;首页&lt;/a&gt;&lt;!-- 路径参数 --&gt;
&lt;a ="['/home',tab.link,{name:'val1'}]"&gt;首页&lt;/a&gt; &lt;!-- 路径对象参数 --&gt;
&lt;a ="['/home']" ="{name:'val1'}"&gt;首页&lt;/a&gt; &lt;!-- 查询参数 --&gt;
&lt;/h1&gt;
&lt;router-outlet&gt;&lt;/router-outlet&gt;&lt;!-- 路由插座,占位标签 --&gt;
&lt;!--<span style="color: rgba(0, 0, 0, 1)">
路由显示的内容是插入到 router</span>-<span style="color: rgba(0, 0, 0, 1)">outlet 的同级的下方节点
而不是在 router</span>-<span style="color: rgba(0, 0, 0, 1)">outlet 中包含
</span>--&gt;
&lt;!--<span style="color: rgba(0, 0, 0, 1)">
当事件处理或者达到某个条件时,可以使用手动跳转
    </span><span style="color: rgba(0, 0, 255, 1)">this</span>.router.navigate(['home'<span style="color: rgba(0, 0, 0, 1)">]);
    </span><span style="color: rgba(0, 0, 255, 1)">this</span>.router.navigate(['home'<span style="color: rgba(0, 0, 0, 1)">,tab.link]);
    </span><span style="color: rgba(0, 0, 255, 1)">this</span>.router.navigate(['home',tab.link,{name:'val1'<span style="color: rgba(0, 0, 0, 1)">}]);
    </span><span style="color: rgba(0, 0, 255, 1)">this</span>.router.navigate(['home'],{queryParams:{name:'val1'<span style="color: rgba(0, 0, 0, 1)">}});
</span>--&gt;</pre>
</div>
<p>控制路由激活状态的样式&nbsp;<code>routerLinkActive</code></p>
<div class="cnblogs_code">
<pre>&lt;h1&gt;
    &lt;a routerLink="/home" routerLinkActive="active"&gt;首页&lt;/a&gt;
    &lt;a routerLink="/news" routerLinkActive="active"&gt;新闻&lt;/a&gt;
&lt;/h1&gt;

&lt;h1&gt;
   &lt;a ="[ '/home' ]" routerLinkActive="active"&gt;首页&lt;/a&gt;
   &lt;a ="[ '/news' ]" routerLinkActive="active"&gt;新闻&lt;/a&gt;
&lt;/h1&gt;
<span style="color: rgba(0, 0, 0, 1)">
.active{
   color:red;
}</span></pre>
</div>
<h3><strong>路由参数</strong></h3>
<p>路径参数读取</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">this</span>.route.paramsMap.subscribe(params =&gt; {...})</pre>
</div>
<p>查询参数读取</p>
<div class="cnblogs_Highlighter">
<pre class="brush:javascript;gutter:true;">this.route.queryParamsMap.subscribe(params =&gt; {...})</pre>
</div>
<p>路由传递一个参数及其接收方法:</p>
<p>传递参数:<code>path:’info/:id’</code></p>
<p>接收参数:</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 0, 1)">constructor(private routerInfo: ActivatedRoute){}
ngOnInit(){
    </span><span style="color: rgba(0, 0, 255, 1)">this</span>.routerInfo.snapshot.params['id'<span style="color: rgba(0, 0, 0, 1)">]
}</span></pre>
</div>
<p>路由传递多个参数及其接收方法:</p>
<p>传递:<code>=‘{id:1,name:‘crm’}’</code></p>
<p>接收参数:</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 0, 1)">constructor(private routerInfo: ActivatedRoute){}
ngOnInit(){
    </span><span style="color: rgba(0, 0, 255, 1)">this</span>.routerInfo.snapshot.params['id'<span style="color: rgba(0, 0, 0, 1)">]
    </span><span style="color: rgba(0, 0, 255, 1)">this</span>.routerInfo.snapshot.params['name'<span style="color: rgba(0, 0, 0, 1)">]
}</span></pre>
</div>
<h3><strong>路由懒加载</strong></h3>
<p>懒加载子模块,子模块需要配置路由设置启动子模块&nbsp;<code>loadChildren</code></p>
<div class="cnblogs_code">
<pre>const routes: Routes =<span style="color: rgba(0, 0, 0, 1)"> [
    {path:</span>'user',loadChildren:'./module/user/user.module#UserModule'<span style="color: rgba(0, 0, 0, 1)"> },
    {path:</span>'product',loadChildren:'./module/product/product.module#ProductModule'<span style="color: rgba(0, 0, 0, 1)">},
    {path:</span>'article',loadChildren:'./module/article/article.module#ArticleModule'<span style="color: rgba(0, 0, 0, 1)">},
    {path:</span>'**',redirectTo:'user'<span style="color: rgba(0, 0, 0, 1)">}
];

</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 上面好像会报错 Error find module </span><span style="color: rgba(0, 128, 0, 1)">
//</span><span style="color: rgba(0, 128, 0, 1)"> 配置懒加载</span>
const routes: Routes =<span style="color: rgba(0, 0, 0, 1)"> [
    {path:</span>'user',loadChildren:()=&gt;import('./module/user/user.module').then(mod=&gt;<span style="color: rgba(0, 0, 0, 1)">mod.UserModule)},
    {path:</span>'article',loadChildren:()=&gt;import('./module/article/article.module').then(mod=&gt;<span style="color: rgba(0, 0, 0, 1)">mod.ArticleModule)},
    {path:</span>'product',loadChildren:()=&gt;import('./module/product/product.module').then(mod=&gt;<span style="color: rgba(0, 0, 0, 1)">mod.ProductModule)},
    {path:</span>'**',redirectTo:'user'<span style="color: rgba(0, 0, 0, 1)">}
];</span></pre>
</div>
<h2><strong>Angular服务</strong></h2>
<p>组件不应该直接获取或保存数据,应该聚焦于展示数据,而把数据访问的职责委托给某个服务</p>
<p>获取数据和视图展示应该相分离,获取数据的方法应该放在服务中</p>
<p>类似 VueX,全局的共享数据(通用数据)及非父子组件传值、共享数据放在服务中</p>
<p>组件之间相互调用各组件里定义的方法</p>
<p>多个组件都用的方法(例如数据缓存的方法)放在服务(service)里</p>
<div class="cnblogs_code">
<pre>import { Injectable } from '@angular/core'<span style="color: rgba(0, 0, 0, 1)">;
@Injectable({
providedIn: </span>'root'<span style="color: rgba(0, 0, 0, 1)">,
})
export class HeroService {
aa </span>= 'abc'<span style="color: rgba(0, 0, 0, 1)">;
constructor(){ }
ngOnInit(){ }
}

import { HeroService } from </span>'../../../services/hero/hero.service'<span style="color: rgba(0, 0, 0, 1)">;
export class AComponent implements OnInit{
constructor(private heroService : HeroService) {} </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">实例化</span>
<span style="color: rgba(0, 0, 0, 1)">ngOnInit(){
    console.log(</span><span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.heroService.aa)
}
}</span></pre>
</div>
<h3><strong>@Injectable()装饰器</strong></h3>
<p>在 Angular 中,要把一个类定义为服务,就要用&nbsp;<code>@Injectable()</code>&nbsp;装饰器来提供元数据,以便让 Angular 把它作为依赖注入到组件中。</p>
<p>同样,也要使用&nbsp;<code>@Injectable ()</code>&nbsp;装饰器来表明一个组件或其它类(比如另一个服务、管道或&nbsp;<code>NgModule</code>)拥有一个依赖。</p>
<p><code>@Injectable ()</code>&nbsp;装饰器把这个服务类标记为依赖注入系统的参与者之一,它是每个 Angular 服务定义中的基本要素。</p>
<p>在未配置好 Angular 的依赖注入器时,Angular 实际上无法将它注入到任何位置。</p>
<p><code>@Injectable ()</code>&nbsp;装饰器具有一个名叫&nbsp;<code>providedIn</code>&nbsp;的元数据选项,<code>providedIn</code>&nbsp;设置为&nbsp;<code>'root'</code>,即根组件中,那么该服务就可以在整个应用程序中使用了。</p>
<p><code>providedIn</code>&nbsp;提供这些值:<code>‘root'</code>&nbsp;、<code>'platform'</code>&nbsp;、<code>'any'</code>&nbsp;、<code>null</code></p>
<p>对于要用到的任何服务,必须至少注册一个提供者。</p>
<p>服务可以在自己的元数据中把自己注册为提供者,可以让自己随处可用,也可以为特定的模块或组件注册提供者。</p>
<p>要注册提供者,就要在服务的&nbsp;<code>@Injectable ()</code>&nbsp;装饰器中提供它的元数据,或者在&nbsp;<code>@NgModule ()</code>&nbsp;或&nbsp;<code>@Component ()</code>&nbsp;的元数据中。</p>
<p>在组件中提供服务时,还可以使用&nbsp;<code>viewProdivers</code>,<code>viewProviders</code>&nbsp;对子组件树不可见</p>
<blockquote>
<p>可以使用不同层级的提供者来配置注入器,也表示该服务的作用范围</p>
<ul>
<li>
<p>Angular 创建服务默认采用的方式:在服务本身的 @Injectable () 装饰器中</p>
</li>
<li>
<p>该服务只在某服务中使用:在 NgModule 的 @NgModule () 装饰器中</p>
</li>
<li>
<p>该服务在某组件中使用:在组件的 @Component () 装饰器中</p>
</li>
</ul>
</blockquote>
<h3><strong>依赖注入</strong></h3>
<p>在项目中,有人提供服务,有人消耗服务,而依赖注入的机制提供了中间的接口,并替消费者创建并初始化处理</p>
<p>消费者只需要知道拿到的是完整可用的服务就好,至于这个服务内部的实现,甚至是它又依赖了怎样的其他服务,都不需要关注。</p>
<p>Angular 通过&nbsp;<code>service</code>共享状态,而这些管理状态和数据的服务便是通过依赖注入的方式进行处理的</p>
<p>Angular 的&nbsp;<code>service</code>&nbsp;的本质就是依赖注入,将<code>service</code>作为一个<code>Injector</code>注入到<code>component</code>中</p>
<p>归根到底,很多时候我们创建服务,是为了维护公用的状态和数据,通过依赖注入的方式来规定哪些组件可共享</p>
<p><img src="https://img.php.cn/upload/article/000/000/024/1e444f9305fcc21dfaaa4a519663e69b-7.png"></p>
<p>正是因为 Angular 提供的这种依赖注入机制,才能在构造函数中直接声明实例化</p>
<div class="cnblogs_Highlighter">
<pre class="brush:javascript;gutter:true;">constructor(private heroService : HeroService) {} // 依赖注入</pre>
</div>
<p><img src="https://img.php.cn/upload/article/000/000/024/98011c1b63c29d8f79032bf5936ea93d-8.png"></p>
<p>先看一下 Angular 中 TS 单文件的注入</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 首先写 @injectable 我们需要注入的东西,比如说 product</span>
<span style="color: rgba(0, 0, 0, 1)">@Injectable()
class Product {
constructor(
    private name: string,
    private color: string,
    private price: number,
) { }
}

class PurchaseOrder {
constructor(private product: Product){ }
}

export class HomeGrandComponent implements OnInit {
constructor() { }
ngOnInit() {
    </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 构造一个 injector 用 create 方法 里面 providers 数组中写我们需要构造的东西</span>
    const injector =<span style="color: rgba(0, 0, 0, 1)"> Injector.create({
      providers: [
      {
          provide: Product,
          </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 构造 Product 在 useFactory 中就会把上面定义的 product 注入到这里</span>
          useFactory: () =&gt;<span style="color: rgba(0, 0, 0, 1)"> {
            </span><span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 0, 255, 1)">new</span> Product('大米手机', '黑色', 2999<span style="color: rgba(0, 0, 0, 1)">);
          },
          deps: []
      },
      {
          provide: PurchaseOrder,
          deps:
      },
      {
          provide: token,
          useValue: { baseUrl: </span>'http://local.dev'<span style="color: rgba(0, 0, 0, 1)"> }
      }
      ]
    });
    console.log(</span>'injector获取product'<span style="color: rgba(0, 0, 0, 1)">, injector.get(PurchaseOrder).getProduct);
    console.log(injector.get(token));
}</span></pre>
</div>
<p>再看一下Angular 中 module 模块的注入</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> .service.ts 中 @Injectable () 依赖注入</span>
<span style="color: rgba(0, 0, 0, 1)">@Injectable()
export class HomeService {
imageSliders: ImageSlider[] </span>=<span style="color: rgba(0, 0, 0, 1)"> [
    {
      imgUrl:</span>''<span style="color: rgba(0, 0, 0, 1)">,
      link: </span>''<span style="color: rgba(0, 0, 0, 1)">,
      caption: </span>''<span style="color: rgba(0, 0, 0, 1)">
    }
]
getBanners() {
    </span><span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.imageSliders;
}
}

</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 使用模块对应的.module.ts 中</span>
<span style="color: rgba(0, 0, 0, 1)">@NgModule({
declarations: [
    HomeDetailComponent,
],
providers:, </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 在 providers 直接写对应服务,直接将服务注入模块</span>
<span style="color: rgba(0, 0, 0, 1)">imports:
})</span></pre>
</div>
<blockquote>
<p>不管是在组件内还是在模块内,我们使用&nbsp;<code>providers</code>&nbsp;的时候,就是进行了一次依赖注入的注册和初始化</p>
<p>其实模块类(<code>NgModule</code>)也和组件一样,在依赖注入中是一个注入器,作为容器提供依赖注入的接口</p>
<p><code>NgModule</code>&nbsp;使我们不需要在一个组件中注入另一个组件,通过模块类(<code>NgModule</code>)可以进行获取和共享</p>
</blockquote>
<h2><strong>Angular 管道</strong></h2>
<p><code>Angular</code>&nbsp;管道是编写可以在 HTML 组件中声明的显示值转换的方法</p>
<p>管道将数据作为输入并将其转换为所需的输出</p>
<p>管道其实就是过滤器,用来转换数据然后显示给用户</p>
<p>管道将整数、字符串、数组和日期作为输入,用&nbsp;<code>|</code>&nbsp;分隔,然后根据需要转换格式,并在浏览器中显示出来</p>
<p>在插值表达式中,可以定义管道并根据情况使用</p>
<p>在&nbsp;<code>Angular</code>&nbsp;应用程序中可以使用许多类型的管道</p>
<h3>内置管道</h3>
<ul>
<li><code>String</code>&nbsp;-&gt;&nbsp;<code>String</code>&nbsp;&nbsp;
<ul>
<li>UpperCasePipe 转换成大写字符</li>
<li>LowerCasePipe 转换成小写字符</li>
<li>TitleCasePipe 转换成标题形式,第一个字母大写,其余小写</li>
</ul>
</li>
<li><code>Number</code>&nbsp;-&gt;&nbsp;<code>String</code>&nbsp;&nbsp;
<ul>
<li>DecimalPipe 根据数字选项和区域设置规则格式化值</li>
<li>PercentPipe 将数字转换为百分比字符串</li>
<li>CurrencyPipe 改变人名币格式</li>
</ul>
</li>
<li><code>Object</code>&nbsp;-&gt;&nbsp;<code>String</code>&nbsp;&nbsp;
<ul>
<li>JsonPipe 对象序列化</li>
<li>DatePipe 日期格式转换</li>
</ul>
</li>
<li><code>Tools</code>&nbsp;&nbsp;
<ul>
<li>SlicePipe 字符串截取</li>
<li>AsyncPipe 从异步回执中解出一个值</li>
<li>I18nPluralPipe 复数化</li>
<li>I18nSelectPipe 显示与当前值匹配的字符串</li>
</ul>
</li>
</ul>
<p><strong>使用方法</strong></p>
<div class="cnblogs_code">
<pre>&lt;div&gt;{{ 'Angular' | uppercase }}&lt;/div&gt;&lt;!-- Output: ANGULAR --&gt;

&lt;div&gt;{{ data | date:'yyyy-MM-dd' }}&lt;/div&gt;&lt;!-- Output: 2022-05-17 --&gt;

&lt;div&gt;{{ { name: 'ccc' } | json }}&lt;/div&gt;&lt;!-- Output: { "name": "ccc" } --&gt;

&lt;!--<span style="color: rgba(0, 0, 0, 1)">
    管道可以接收任意数量的参数,使用方式是在管道名称后面添加: 和参数值
    若需要传递多个参数则参数之间用冒号隔开
</span>--&gt;

&lt;!-- 可以将多个管道连接在一起,组成管道链对数据进行处理 --&gt;
&lt;div&gt;{{ 'ccc' | slice:0:1 | uppercase }}&lt;/div&gt;</pre>
</div>
<h3><strong>自定义管道</strong></h3>
<p>管道本质上就是个类,在这个类里面去实现&nbsp;<code>PipeTransfrom</code>&nbsp;接口的&nbsp;<code>transform</code>&nbsp;这个方法</p>
<ul>
<li>使用&nbsp;<code>@Pipe</code>&nbsp;装饰器定义&nbsp;<code>Pipe</code>&nbsp;的&nbsp;<code>metadata</code>&nbsp;信息,如&nbsp;<code>Pipe</code>&nbsp;的名称 - 即&nbsp;<code>name</code>&nbsp;属性</li>
<li>实现&nbsp;<code>PipeTransform</code>&nbsp;接口中定义的&nbsp;<code>transform</code>&nbsp;方法</li>
</ul>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 引入PipeTransform是为了继承transform方法</span>
import { Pipe, PipeTransform } form '@angular/core'<span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> name属性值惯用小驼峰写法, name的值为html中 | 后面的名称</span>
@Pipe({ name: 'sexReform'<span style="color: rgba(0, 0, 0, 1)"> })
export class SexReformPipe implements PipeTransform {
    transform(value: string, args</span>?<span style="color: rgba(0, 0, 0, 1)">: any): string {
    </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> value的值为html中 | 前面传入的值, args为名称后传入的参数</span>
      <span style="color: rgba(0, 0, 255, 1)">switch</span><span style="color: rgba(0, 0, 0, 1)">(value){
            </span><span style="color: rgba(0, 0, 255, 1)">case</span> 'male': <span style="color: rgba(0, 0, 255, 1)">return</span> '男'<span style="color: rgba(0, 0, 0, 1)">;
            </span><span style="color: rgba(0, 0, 255, 1)">case</span> 'female': <span style="color: rgba(0, 0, 255, 1)">return</span> '女'<span style="color: rgba(0, 0, 0, 1)">;
            </span><span style="color: rgba(0, 0, 255, 1)">default</span>: <span style="color: rgba(0, 0, 255, 1)">return</span> '雌雄同体'<span style="color: rgba(0, 0, 0, 1)">;
      }
    }
}

</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> demo.component.ts</span>
<span style="color: rgba(0, 0, 0, 1)">export Class DemoComponent {
    sexValue </span>= 'female'<span style="color: rgba(0, 0, 0, 1)">;
}

</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> demo.component.html</span>
&lt;span&gt;{{ sexValue | sexReform }}&lt;/span&gt;

<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 浏览器输出</span>
<span style="color: rgba(0, 0, 0, 1)">女

</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 管道可以链式使用,还可以传参</span>
&lt;span&gt; {{date | date: 'fullDate' | uppercase}} &lt;/span&gt;
<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 每一个自定义管道都需要实现 PipeTransform 接口,这个接口非常简单,只需要实现 transform 方法即可。</span><span style="color: rgba(0, 128, 0, 1)">
//</span><span style="color: rgba(0, 128, 0, 1)"> transform()方法参数格式 - transform(value: string, args1: any, args2?: any): </span><span style="color: rgba(0, 128, 0, 1)">
//</span><span style="color: rgba(0, 128, 0, 1)"> value为传入的值(即为需要用此管道处理的值, | 前面的值); </span><span style="color: rgba(0, 128, 0, 1)">
//</span><span style="color: rgba(0, 128, 0, 1)"> args 为传入的参数(?:代表可选);</span><span style="color: rgba(0, 128, 0, 1)">
//</span><span style="color: rgba(0, 128, 0, 1)"> html 中使用管道格式 - {{ 数据 | 管道名 : 参数1 : 参数2 }}</span><span style="color: rgba(0, 128, 0, 1)">
//</span><span style="color: rgba(0, 128, 0, 1)"> 与 component 一样,pipe 需要先在 declarations 数组中声明后使用</span></pre>
</div>
<h2><strong>Angular操作DOM</strong></h2>
<h3><strong>原生JS操作</strong></h3>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 0, 1)">ngAfterViewInit(){
   </span><span style="color: rgba(0, 0, 255, 1)">var</span> boxDom:any=document.getElementById('box'<span style="color: rgba(0, 0, 0, 1)">);
   boxDom.style.color</span>='red'<span style="color: rgba(0, 0, 0, 1)">;
}</span></pre>
</div>
<h3><strong>ElementRef</strong></h3>
<p><code>ElementRef&nbsp;</code>是对视图中某个原生元素的包装类</p>
<p>因为 DOM 元素不是 Angular 中的类,所以需要一个包装类以便在 Angular 中使用和标识其类型</p>
<p><code>ElementRef</code>&nbsp;的背后是一个可渲染的具体元素。在浏览器中,它通常是一个 DOM 元素</p>
<div class="cnblogs_code">
<pre>class ElementRef&lt;T&gt;<span style="color: rgba(0, 0, 0, 1)"> {
constructor(nativeElement: T)
nativeElement: T</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">背后的原生元素,如果不支持直接访问原生元素,则为 null(比如:在 Web Worker 环境下运行此应用的时候)。</span>
}</pre>
</div>
<p>当需要直接访问 DOM 时,请把本 API 作为最后选择 。优先使用 Angular 提供的模板和数据绑定机制</p>
<p>如果依赖直接访问 DOM 的方式,就可能在应用和渲染层之间产生紧耦合。这将导致无法分开两者,也就无法将应用发布到 Web Worker 中</p>
<h3><strong>ViewChild</strong></h3>
<p>使用模板和数据绑定机制,使用&nbsp;<code>@viewChild</code></p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 模版中给 DOM 起一个引用名字,以便可以在组件类或模版中进行引用 &lt;div #myattr&gt;&lt;/div&gt;</span>

<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 引入 ViewChild</span>
import { ViewChild,ElementRef } from '@angular/core'<span style="color: rgba(0, 0, 0, 1)">;

</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 用 ViewChild 绑定 DOM    </span>
@ViewChild('myattr'<span style="color: rgba(0, 0, 0, 1)">) myattr: ElementRef;

</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 在 ngAfterViewInit 生命周期函数里可以很安全的获取 ViewChild 引用的 DOM</span>
<span style="color: rgba(0, 0, 0, 1)">ngAfterViewInit(){
   let attrEl </span>= <span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.myattr.nativeElement;
}</span></pre>
</div>
<p>父组件中可以通过&nbsp;<code>ViewChild</code>&nbsp;调用子组件的方法</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 给子组件定义一个名称</span>
&lt;app-footer #footerChild&gt;&lt;/app-footer&gt;

<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 引入 ViewChild</span>
import { Component, OnInit ,ViewChild} from '@angular/core'<span style="color: rgba(0, 0, 0, 1)">;

</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> ViewChild 和子组件关联起来 </span><span style="color: rgba(0, 128, 0, 1)">
//</span><span style="color: rgba(0, 128, 0, 1)"> 如果想引用模版中的 Angular 组件,ViewChild 中可以使用引用名,也可以使用组件类型</span>
@ViewChild('footerChild'<span style="color: rgba(0, 0, 0, 1)">) footer;

</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> @ViewChild('imageSlider', { static: true }) // static指定是动态还是静态,在*ngFor或者*ngIf中是动态,否则即为静态,动态为 true</span>

<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 调用子组件</span>
<span style="color: rgba(0, 0, 0, 1)">run(){
   </span><span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.footer.footerRun();
}</span></pre>
</div>
<p>引用多个模版元素,可以用<code>@ViewChildren</code>,在<code>ViewChildren</code>中可以使用引用名</p>
<p>或者使用 Angular 组件/指令的类型,声明类型为&nbsp;<code>QueryList&lt;?&gt;</code></p>
<div class="cnblogs_code">
<pre>&lt;<span style="color: rgba(0, 0, 0, 1)">img
#img
</span>*ngFor="let slider of sliders"<span style="color: rgba(0, 0, 0, 1)">
</span>="slider.imgUrl"<span style="color: rgba(0, 0, 0, 1)">
</span>="slider.capiton"
&gt;

<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 使用 ViewChildren 引用获取</span>
<span style="color: rgba(0, 0, 0, 1)">@ViewChildren(’img‘);

</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 使用类型引用获取</span>
imgs: QueryList&lt;ElementRef&gt;;</pre>
</div>
<h3><strong>Renderer2</strong></h3>
<p><code>Renderer2</code>&nbsp;是 Angular 提供的操作&nbsp;<code>element</code>&nbsp;的抽象类,使用该类提供的方法,能够实现在不直接接触 DOM 的情况下操作页面上的元素。</p>
<p><code>Renderer2</code>&nbsp;的常用方法:</p>
<ul>
<li><code>addClass</code>&nbsp;/<code>removeClass</code>&nbsp;在&nbsp;<code>directive</code>&nbsp;的宿主元素添加或删除&nbsp;<code>class</code></li>
</ul>
<div class="cnblogs_code">
<pre>import { Directive, Renderer2, ElementRef, OnInit } from '@angular/core'<span style="color: rgba(0, 0, 0, 1)">;

@Directive({
    selector: </span>''<span style="color: rgba(0, 0, 0, 1)">
})

export class TestRenderer2Directive implements OnInit {
    constructor(private renderer: Renderer2, private el: ElementRef) {} </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 实例化</span>
<span style="color: rgba(0, 0, 0, 1)">
    ngOnInit() {
    </span><span style="color: rgba(0, 0, 255, 1)">this</span>.renderer.addClass(<span style="color: rgba(0, 0, 255, 1)">this</span>.el.nativeElement, 'test-renderer2'<span style="color: rgba(0, 0, 0, 1)">);
    </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> this.renderer.removeClass(this.el.nativeElement, 'old-class');</span>
<span style="color: rgba(0, 0, 0, 1)">    }
}</span></pre>
</div>
<ul>
<li><code>createElement</code>&nbsp;/<code>appendChild</code>/<code>createText</code>&nbsp;创建 DIV 元素,插入文本内容,并将其挂载到宿主元素上</li>
</ul>
<div class="cnblogs_code">
<pre>import { Directive, Renderer2, ElementRef, OnInit } from '@angular/core'<span style="color: rgba(0, 0, 0, 1)">;

constructor(private renderer: Renderer2, private el: ElementRef) {}

ngOnInit() {
    const div </span>= <span style="color: rgba(0, 0, 255, 1)">this</span>.renderer.createElement('div'<span style="color: rgba(0, 0, 0, 1)">);
    const text </span>= <span style="color: rgba(0, 0, 255, 1)">this</span>.renderer.createText('Hello world!'<span style="color: rgba(0, 0, 0, 1)">);
   
    </span><span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.renderer.appendChild(div, text);
    </span><span style="color: rgba(0, 0, 255, 1)">this</span>.renderer.appendChild(<span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.el.nativeElement, div);
}</span></pre>
</div>
<ul>
<li><code>setAttribute</code>&nbsp;/<code>removeAttribute</code>&nbsp;在宿主元素上添加或删除&nbsp;<code>attribute</code></li>
</ul>
<div class="cnblogs_code">
<pre>import { Directive, Renderer2, ElementRef, OnInit } from '@angular/core'<span style="color: rgba(0, 0, 0, 1)">;

constructor(private renderer: Renderer2, private el: ElementRef) {}

ngOnInit() {
    </span><span style="color: rgba(0, 0, 255, 1)">this</span>.renderer.setAttribute(<span style="color: rgba(0, 0, 255, 1)">this</span>.el.nativeElement, 'aria-hidden', 'true'<span style="color: rgba(0, 0, 0, 1)">);
}</span></pre>
</div>
<ul>
<li><code>setStyle</code>&nbsp;/<code>removeStyle</code>&nbsp;在宿主元素上添加&nbsp;<code>inline-style</code></li>
</ul>
<div class="cnblogs_code">
<pre>import { Directive, Renderer2, ElementRef, OnInit } from '@angular/core'<span style="color: rgba(0, 0, 0, 1)">;

constructor(private renderer: Renderer2, private el: ElementRef) {}

ngOnInit() {
    </span><span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.renderer.setStyle(
      </span><span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.el.nativeElement,
      </span>'border-left'<span style="color: rgba(0, 0, 0, 1)">,
      </span>'2px dashed olive'<span style="color: rgba(0, 0, 0, 1)">
    );
}</span></pre>
</div>
<p>移除&nbsp;<code>inline-style</code>&nbsp;:</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 0, 1)">constructor(private renderer: Renderer2, private el: ElementRef) {}

ngOnInit() {
    </span><span style="color: rgba(0, 0, 255, 1)">this</span>.renderer.removeStyle(<span style="color: rgba(0, 0, 255, 1)">this</span>.el.nativeElement, 'border-left'<span style="color: rgba(0, 0, 0, 1)">);
}</span></pre>
</div>
<ul>
<li><code>setProperty</code>&nbsp;设置宿主元素的&nbsp;<code>property</code>&nbsp;的值</li>
</ul>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 0, 1)">constructor(private renderer: Renderer2, private el: ElementRef) {}

ngOnInit() {
    </span><span style="color: rgba(0, 0, 255, 1)">this</span>.renderer.setProperty(<span style="color: rgba(0, 0, 255, 1)">this</span>.el.nativeElement, 'alt', 'Cute alligator'<span style="color: rgba(0, 0, 0, 1)">);
}</span></pre>
</div>
<blockquote>
<p>直接操作DOM,<code>Angular</code>不推荐。尽量采用&nbsp;<code>@viewChild</code>&nbsp;和&nbsp;<code>renderer2</code>&nbsp;组合,<code>Angular</code>推荐使用&nbsp;<code>constructor(private rd2: Renderer2) {}</code>&nbsp;依赖注入,</p>
</blockquote>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 0, 1)">import {
Component,
OnInit,
Renderer2,
ViewChild,
} from </span>'@angular/core'<span style="color: rgba(0, 0, 0, 1)">;
import { AboxItemComponent } from </span>'./abox-item/abox-item.component'<span style="color: rgba(0, 0, 0, 1)">;

@Component({
selector: </span>'app-abox'<span style="color: rgba(0, 0, 0, 1)">,
templateUrl: </span>'./abox.component.html'<span style="color: rgba(0, 0, 0, 1)">,
styleUrls: [</span>'./abox.component.less'<span style="color: rgba(0, 0, 0, 1)">],
})
export class AboxComponent implements OnInit {
private container;
activeIndex: number;
@ViewChild(</span>'containers'<span style="color: rgba(0, 0, 0, 1)">) containers: any;
constructor(private rd2: Renderer2) {}

ngOnInit(): </span><span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)"> {}

ngAfterViewInit(): </span><span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)"> {
    </span><span style="color: rgba(0, 0, 255, 1)">this</span>.container = <span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.containers.nativeElement;
    </span><span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.initCarouselWidth();
}
   
initCarouselWidth() {
    </span><span style="color: rgba(0, 0, 255, 1)">this</span>.rd2.setStyle(<span style="color: rgba(0, 0, 255, 1)">this</span>.container, 'width', '100px'<span style="color: rgba(0, 0, 0, 1)">);
}
}</span></pre>
</div>
<h2><strong>Angular网络请求</strong></h2>
<h3><strong>HttpClient</strong></h3>
<p>需导入&nbsp;<code>HttpClientModule</code>&nbsp;,只在根模块中导入,并且整个应用只需导入一次,不用在其他模块导入</p>
<p>在构造函数中注入<code>HttpClient</code>,<code>get/post</code>方法对应HTTP方法,这些方法是泛型的,可以直接把返回的JSON转换成对应类型。若是不规范的请求,使用<code>request</code>方法</p>
<p>返回的值是&nbsp;<code>Observable</code>,必须订阅才会发送请求,否则不会发送</p>
<p><strong>get 请求数据</strong></p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 在 app.module.ts 中引入 HttpClientModule 并注入</span>
import {HttpClientModule} from '@angular/common/http'<span style="color: rgba(0, 0, 0, 1)">;
imports: [
BrowserModule,
HttpClientModule
]

</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 在用到的地方引入 HttpClient 并在构造函数声明</span>
import {HttpClient} from "@angular/common/http"<span style="color: rgba(0, 0, 0, 1)">;
constructor(private http: HttpClient,private cd: ChangeDetectorRef) { } </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 依赖注入</span>

<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> get 请求数据</span>
<span style="color: rgba(0, 0, 255, 1)">var</span> api = "http://baidu.com/api/productlist"<span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 0, 255, 1)">this</span>.http.get(api).subscribe(response =&gt;<span style="color: rgba(0, 0, 0, 1)"> {
console.log(response);
</span><span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.cd.markForCheck();
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 如果改变了脏值检测的变更原则 changeDetection: ChangeDetectionStrategy.OnPush</span>
<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 则需要使用 this.cd.markForCheck() 手动提醒 Angular 这里需要进行脏值检测</span>
});</pre>
</div>
<p><strong>post 提交数据</strong></p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 在 app.module.ts 中引入 HttpClientModule 并注入</span>
import {HttpClientModule} from '@angular/common/http'<span style="color: rgba(0, 0, 0, 1)">;
imports: [
   BrowserModule,
   HttpClientModule
]

</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 在用到的地方引入 HttpClient 、HttpHeaders 并在构造函数声明 HttpClient</span>
import {HttpClient,HttpHeaders} from "@angular/common/http"<span style="color: rgba(0, 0, 0, 1)">;
constructor(private http:HttpClient) { } </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 实例化</span>

<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> post 提交数据</span>
const httpOptions =<span style="color: rgba(0, 0, 0, 1)"> {
    headers: </span><span style="color: rgba(0, 0, 255, 1)">new</span> HttpHeaders({ 'Content-Type': 'application/json'<span style="color: rgba(0, 0, 0, 1)"> })
};
</span><span style="color: rgba(0, 0, 255, 1)">var</span> api = "http://127.0.0.1:4200/doLogin"<span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 0, 255, 1)">this</span>.http.post(api,{username:'瑞萌萌',age:'22'},httpOptions).subscribe(response =&gt;<span style="color: rgba(0, 0, 0, 1)"> {
      console.log(response);
});</span></pre>
</div>
<p><strong>Jsonp请求数据</strong></p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 在 app.module.ts 中引入 HttpClientModule、HttpClientJsonpModule 并注入</span>
import {HttpClientModule,HttpClientJsonpModule} from'@angular/common/http'<span style="color: rgba(0, 0, 0, 1)">;
imports: [
   BrowserModule,
   HttpClientModule,
   HttpClientJsonpModule
]

</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 在用到的地方引入 HttpClient 并在构造函数声明</span>
import {HttpClient} from "@angular/common/http"<span style="color: rgba(0, 0, 0, 1)">;
constructor(private http:HttpClient) { } </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 实例化</span>

<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> jsonp 请求数据</span>
<span style="color: rgba(0, 0, 255, 1)">var</span> api = "http://baidu.com/api/productlist"<span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 0, 255, 1)">this</span>.http.jsonp(api,'callback').subscribe(response =&gt;<span style="color: rgba(0, 0, 0, 1)"> {
   console.log(response);
});</span></pre>
</div>
<h3><strong>拦截器</strong></h3>
<p>Angular 拦截器是 Angular 应用中全局捕获和修改 HTTP 请求和响应的方式,例如携带&nbsp;<code>Token</code>&nbsp;和捕获&nbsp;<code>Error</code></p>
<p>前提是只能拦截使用&nbsp;<code>HttpClientModule</code>&nbsp;发出的请求,如果使用&nbsp;<code>axios</code>&nbsp;则拦截不到</p>
<p><strong>创建拦截器</strong></p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 使用命令 ng g interceptor name,在这里创建拦截器 ng g interceptor LanJieQi</span><span style="color: rgba(0, 128, 0, 1)">
//</span><span style="color: rgba(0, 128, 0, 1)"> cli 生成拦截器是没有简写方式的</span>
<span style="color: rgba(0, 0, 0, 1)">
import { Injectable } from </span>'@angular/core'<span style="color: rgba(0, 0, 0, 1)">;
import {
HttpRequest,
HttpHandler,
HttpEvent,
HttpInterceptor
} from </span>'@angular/common/http'<span style="color: rgba(0, 0, 0, 1)">;
import { Observable } from </span>'rxjs'<span style="color: rgba(0, 0, 0, 1)">;

@Injectable()
export class LanJieQiInterceptor implements HttpInterceptor {
constructor() {}
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 默认的 intercept() 方法只是单纯的将请求转发给下一个拦截器(如果有),并最终返回 HTTP 响应体的 Observable</span>
<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> request: HttpRequest&lt;unknown&gt; 表示请求对象,包含了请求相关的所有信息,unknown指定请求体body的类型</span>
<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> next: HttpHandler 请求对象修改完成,将修改后的请求对象通过next中的handle方法传回真正发送请求的方法中</span>
intercept(request: HttpRequest&lt;unknown&gt;, next: HttpHandler): Observable&lt;HttpEvent&lt;unknown&gt;&gt;<span style="color: rgba(0, 0, 0, 1)">   {
    </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> next 对象表示拦截器链表中的下一个拦截器(在应用中可以设置多个拦截器)</span>
    <span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> next.handle(request);
}
}</span></pre>
</div>
<p><strong>注入拦截器</strong></p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 在 @NgModule 模块中注入拦截器</span><span style="color: rgba(0, 128, 0, 1)">
//</span><span style="color: rgba(0, 128, 0, 1)"> 拦截器也是一个由 Angular 依赖注入 (DI) 系统管理的服务,也必须先提供这个拦截器类,才能使用它</span><span style="color: rgba(0, 128, 0, 1)">
//</span><span style="color: rgba(0, 128, 0, 1)"> 由于拦截器是 HttpClient 服务的依赖,所以必须在提供 HttpClient 的同一个(或其各级父注入器)注入器中提供这些拦截器</span>
<span style="color: rgba(0, 0, 0, 1)">@NgModule({
imports: [
    HttpClientModule
    </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> others...</span>
<span style="color: rgba(0, 0, 0, 1)">],
providers: [
    {
      provide: HTTP_INTERCEPTORS,
      useClass: LanJieQiInterceptor,
      </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> multi: true 表明 HTTP_INTERCEPTORS 是一个多重提供者的令牌,表示这个令牌可以注入多个拦截器</span>
      multi: <span style="color: rgba(0, 0, 255, 1)">true</span><span style="color: rgba(0, 0, 0, 1)">
    },
],
bootstrap:
})

export class AppModule { }</span></pre>
</div>
<p><strong>请求头拦截</strong></p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 0, 1)">@Injectable()export class LanJieQiInterceptor implements HttpInterceptor {
constructor() {}
intercept(request: HttpRequest</span>&lt;unknown&gt;, next: HttpHandler): Observable&lt;HttpEvent&lt;unknown&gt;&gt;<span style="color: rgba(0, 0, 0, 1)">   {
    </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 为了统一设置请求头,需要修改请求</span>
    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 但 HttpRequest 和 HttpResponse 实例的属性却是只读(readonly)的</span>
    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 所以修改前需要先 clone 一份,修改这个克隆体后再把它传给 next.handle()</span>
    let req =<span style="color: rgba(0, 0, 0, 1)"> request.clone({
      setHeaders:{
          token:</span>"123456" <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 在请求头中增加 token:123456</span>
<span style="color: rgba(0, 0, 0, 1)">      }
            </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> setHeaders 和 headers: request.headers.set('token', '123456') 一致</span>
<span style="color: rgba(0, 0, 0, 1)">      })
      </span><span style="color: rgba(0, 0, 255, 1)">return</span> next.handle(req)<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 将修改后的请求返回给应用</span>
}}</pre>
</div>
<p><strong>响应捕获</strong></p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 0, 1)">@Injectable()
export class LanJieQiInterceptor implements HttpInterceptor {
constructor() {}
intercept(request: HttpRequest</span>&lt;unknown&gt;, next: HttpHandler): Observable&lt;HttpEvent&lt;unknown&gt;&gt;<span style="color: rgba(0, 0, 0, 1)">   {
    </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 为了统一设置请求头,需要修改请求</span>
    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 但 HttpRequest 和 HttpResponse 实例的属性却是只读(readonly)的</span>
    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 所以修改前需要先 clone 一份,修改这个克隆体后再把它传给 next.handle()</span>
    let req =<span style="color: rgba(0, 0, 0, 1)"> request.clone({
      setHeaders:{
          token:</span>"123456" <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 在请求头中增加 token:123456</span>
<span style="color: rgba(0, 0, 0, 1)">      }
            </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> setHeaders 和 headers: request.headers.set('token', '123456') 一致</span>
<span style="color: rgba(0, 0, 0, 1)">      })
      </span><span style="color: rgba(0, 0, 255, 1)">return</span> next.handle(req)<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 将修改后的请求返回给应用</span>
<span style="color: rgba(0, 0, 0, 1)">}
}</span></pre>
</div>
<blockquote>
<p>如果有多个拦截器,请求顺序是按照配置顺序执行,响应拦截则是相反的顺序</p>
<p>如果提供拦截器的顺序是先 A再 B再 C,那么请求阶段的执行顺序就是 A-&gt;B-&gt;C,而响应阶段的执行顺序则是 C-&gt;B-&gt;A</p>
</blockquote>
<h2><strong>Angular表单</strong></h2>
<h3><strong>模版驱动表单</strong></h3>
<p>模板驱动表单在往应用中添加简单的表单时非常有用,但是不像响应式表单那么容易扩展</p>
<p>如果有非常基本的表单需求和简单到能用模板管理的逻辑,就使用模板驱动表单</p>
<blockquote>
<p>响应式表单和模板驱动表单共享了一些底层构造块:</p>
<p><code>FormControl</code>&nbsp;实例用于追踪单个表单控件的值和验证状态</p>
<p><code>FormGroup</code>&nbsp;用于追踪一个表单控件组的值和状态</p>
<p><code>FormArray</code>&nbsp;用于追踪表单控件数组的值和状态,有长度属性,通常用来代表一个可以增长的字段集合</p>
<p><code>ControlValueAccessor</code>&nbsp;用于在 Angular 的&nbsp;<code>FormControl</code>&nbsp;实例和原生 DOM 元素之间创建一个桥梁</p>
</blockquote>
<p><code>FormControl</code>&nbsp;和&nbsp;<code>FormGroup</code>&nbsp;是 angular 中两个最基本的表单对象</p>
<p><code>FormControl</code>&nbsp;代表单一的输入字段,它是 Angular 表单中最小单员,它封装了这些字段的值和状态,比如是否有效、是否脏(被修改过)或是否有错误等</p>
<p><code>FormGroup</code>&nbsp;可以为一组&nbsp;<code>FormControl</code>&nbsp;提供总包接口(wrapper interface),来管理多个&nbsp;<code>FormControl</code></p>
<p>当我们试图从&nbsp;<code>FormGroup</code>&nbsp;中获取 value 时,会收到一个 “键值对” 结构的对象</p>
<p>它能让我们从表单中一次性获取全部的值而无需逐一遍历&nbsp;<code>FormControl</code>,使用起来相当顺手</p>
<p><code>FormGroup</code>&nbsp;和&nbsp;<code>FormControl</code>&nbsp;都继承自同一个祖先&nbsp;<code>AbstractControltractControl</code>(这是&nbsp;<code>FormControl</code>,<code>FormGroup</code>&nbsp;和&nbsp;<code>FormArray</code>&nbsp;的基类)</p>
<p>首先加载&nbsp;<code>FormsModule</code></p>
<div class="cnblogs_Highlighter">
<pre class="brush:javascript;gutter:true;">// 先在 NgModule 中导入了 FormsModule 表单库
// FormsModule 为我们提供了一些模板驱动的指令,例如:ngModel、NgForm
import {
FormsModule
} from '@angular/forms';

@NgModule({
declarations: [
    FormsDemoApp,
    DemoFormSku,
    // ... our declarations here
],
imports: [
    BrowserModule,
    FormsModule,
],
bootstrap: [ FormsDemoApp ]
})
class FormsDemoAppModule {}</pre>
</div>
<p>接下来创建一个模版表单</p>
<div class="cnblogs_code">
<pre> &lt;div&gt;
      &lt;h2&gt;基础表单:商品名称&lt;/h2&gt;
      &lt;form #f="ngForm" (ngSubmit)="onSubmit(f.value)"&gt;
      &lt;div class="sku"&gt;
          &lt;label <span style="color: rgba(0, 0, 255, 1)">for</span>="skuInput"&gt;商品名称:&lt;/label&gt;
          &lt;<span style="color: rgba(0, 0, 0, 1)">input
            type</span>="text"<span style="color: rgba(0, 0, 0, 1)">
            id</span>="skuInput"<span style="color: rgba(0, 0, 0, 1)">
            placeholder</span>="商品名称"<span style="color: rgba(0, 0, 0, 1)">
            name</span>="sku" <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">使用form时必须定义,可以理解为当前控件的名字</span>
<span style="color: rgba(0, 0, 0, 1)">            ngModel
          </span>/&gt;
      &lt;/div&gt;
      &lt;button&gt;提交&lt;/button&gt;
      &lt;/form&gt;
    &lt;/div&gt;</pre>
</div>
<p>我们导入了&nbsp;<code>FormsModule</code>,因此可以在视图中使用&nbsp;<code>NgForm</code>&nbsp;了</p>
<p>当这些指令在视图中可用时,它就会被附加到任何能匹配其 selector 的节点上</p>
<p><code>NgForm</code>&nbsp;做了一件便利但隐晦的工作:它的选择器包含 form 标签(而不用显式添加&nbsp;<code>ngForm</code>&nbsp;属性)</p>
<p>这意味着当导入&nbsp;<code>FormsModule</code>&nbsp;时候,<code>NgForm</code>&nbsp;就会被自动附加到视图中所有的标签上</p>
<p><code>NgForm</code>&nbsp;提供了两个重要的功能:</p>
<ul>
<li>一个&nbsp;<code>ngForm</code>&nbsp;的&nbsp;<code>FormGroup</code>&nbsp;对象</li>
<li>一个输出事件 (<code>ngSubmit</code>)</li>
</ul>
<div class="cnblogs_code">
<pre> &lt;form #f="ngForm" (ngSubmit)="onSubmit(f.value)" &gt;
&lt;!--<span style="color: rgba(0, 0, 0, 1)">
    这里使用了 #f</span>=“ngForm”,#v=<span style="color: rgba(0, 0, 0, 1)">thing 的意思是我们希望在当前视图中创建一个局部变量
    这里为视图中的 ngForm 创建了一个别名,并绑定到变量 #f
    这个 ngForm 是由 NgForm 指令导出的
    ngForm 的类型的对象是 FormGroup 类型的
    这意味着可以在视图中把变量 f 当作 FormGroup 使用,而这也正是我们在输出事件 (ngSubmit) 中的使用方法
    在表单中绑定 ngSubmit 事件 (ngSubmit)</span>=<span style="color: rgba(0, 0, 0, 1)">“onSubmit (f.value)“
    (ngSubmit) 来自 NgForm 指令
    onSubmit() 将会在组件类中进行定义
    f 就是 FormGroup ,而 .value 会以键值对的形式返回 FormGroup 中所有控件的值
   
    总结:当提交表单时,将会以该表单的值作为参数,调用组件实例上的 `onSubmit` 方法
</span>--&gt;</pre>
</div>
<p><code>NgModel</code>&nbsp;会创建一个新的&nbsp;<code>FormControl</code>&nbsp;对象,把它自动添加到父&nbsp;<code>FormGroup</code>&nbsp;上(这里也就是 form 表单对象)</p>
<p>并把这个&nbsp;<code>FormControl</code>&nbsp;对象绑定到一个 DOM 上</p>
<p>也就是说,它会在视图中的&nbsp;<code>input</code>&nbsp;标签和&nbsp;<code>FormControl</code>&nbsp;对象之间建立关联</p>
<p>这种关联是通过&nbsp;<code>name</code>&nbsp;属性建立的,在本例中是&nbsp;<code>"name"</code></p>
<h3><strong>响应式表单</strong></h3>
<p>使用&nbsp;<code>ngForm</code>&nbsp;构建&nbsp;<code>FormControl</code>&nbsp;和&nbsp;<code>FormGroup</code>&nbsp;很方便,但是无法提供定制化选项,因此引入响应式表单</p>
<p>响应式表单提供了一种模型驱动的方式来处理表单输入,其中的值会随时间而变化</p>
<p>使用响应式表单时,通过编写 TypeScript 代码而不是 HTML 代码来创建一个底层的数据模型</p>
<p>在这个模型定义好以后,使用一些特定的指令将模板上的 HTML 元素与底层的数据模型连接在一起</p>
<p><code>FormBuilder</code>&nbsp;是一个名副其实的表单构建助手(可以把他看作一个 “工厂” 对象)</p>
<p>在先前的例子中添加一个&nbsp;<code>FormBuilder</code>,然后在组件定义类中使用&nbsp;<code>FormGroup</code></p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 先在 NgModule 中导入了 ReactiveFormsModule 表单库</span>
<span style="color: rgba(0, 0, 0, 1)">import {
ReactiveFormsModule
} from </span>'@angular/forms'<span style="color: rgba(0, 0, 0, 1)">;
@NgModule({
imports: [
    FormsModule,
    ReactiveFormsModule
]
})

</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 使用 formGroup 和 formControl 指令来构建这个组件,需要导入相应的类</span>
<span style="color: rgba(0, 0, 0, 1)">import {
FormBuilder,
FormGroup,
ReactiveFormsModule
} from </span>'@angular/forms'<span style="color: rgba(0, 0, 0, 1)">;

</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 在组件类上注入一个从 FormBuilder 类创建的对象实例,并把它赋值给 fb 变量(来自构造函数)</span>
<span style="color: rgba(0, 0, 0, 1)">export class DemoFormSkuBuilder {
myForm: FormGroup;</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> myForm 是 FormGroup 类型</span>
<span style="color: rgba(0, 0, 0, 1)">constructor(fb: FormBuilder) {
    </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> FormBuilder 中的 group 方法用于创建一个新的 FormGroup</span>
    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> group 方法的参数是代表组内各个 FormControl 的键值对</span>
    <span style="color: rgba(0, 0, 255, 1)">this</span>.myForm = fb.group({<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 调用 fb.group () 来创建 FormGroup</span>
      <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 设置一个名为 sku 的控件,控件的默认值为 "123456"</span>
      'sku': ['123456'<span style="color: rgba(0, 0, 0, 1)">]
    });
}
onSubmit(value: string): </span><span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)"> {
    console.log(</span>'submit value:'<span style="color: rgba(0, 0, 0, 1)">, value);
}
}</span></pre>
</div>
<p>在视图表单中使用自定义的&nbsp;<code>FormGroup</code></p>
<div class="cnblogs_code">
<pre>&lt;h2 class="ui header"&gt;Demo Form: Sku <span style="color: rgba(0, 0, 255, 1)">with</span> Builder&lt;/h2&gt;
&lt;!--<span style="color: rgba(0, 0, 0, 1)">
    当导入 FormsModule 时,ngForm 就会自动创建它自己的 FormGroup
    但这里不希望使用外部的 FormGroup,而是使用 FormBuilder 创建这个 myForm 实例变量
    Angular提供了 formGroup 指令,能让我们使用现有的 FormGroup
    NgForm 不会应用到带 formGroup 属性的节点上
    这里我们告诉Angular,想用 myForm 作为这个表单的 FormGroup
</span>--&gt;
&lt;form ="myForm"
&lt;label <span style="color: rgba(0, 0, 255, 1)">for</span>="skuInput"&gt; SKU &lt;/label&gt;
&lt;input type="text"<span style="color: rgba(0, 0, 0, 1)">
   id</span>="skuInput"<span style="color: rgba(0, 0, 0, 1)">
   placeholder</span>="SKU"<span style="color: rgba(0, 0, 0, 1)">
   </span>="myForm.controls['sku']"&gt;
&lt;!--<span style="color: rgba(0, 0, 0, 1)">
    将 FormControl 绑定到 input 标签上 :
    ngModel 会创建一个新的 FormControl 对象并附加到父 FormGroup 中
    但在例子中,我们已经用 FormBuilder 创建了自己的 FormControl
    要将现有的 FormControl 绑定到 input 上,可以用 formControl 指令
    将 input 标签上的 formControl 指令指向 myForm.controls 上现有的 FormControl 控件 sku
</span>--&gt;</pre>
</div>
<blockquote>
<p>记住以下两点:</p>
<ol>
<li>如果想隐式创建新的 FormGroup 和 FormControl,使用:ngForm、ngModel</li>
<li>如果要绑定一个现有的 FormGroup 和 FormControl,使用:formGroup、formControl</li>
</ol></blockquote>
<h3>表单验证</h3>
<p>用户输入的数据格式并不总是正确的,如果有人输入错误的数据格式,我们希望给他反馈并阻止他提交表单</p>
<p>因此,我们要用到验证器,由&nbsp;<code>validators</code>&nbsp;模块提供</p>
<p><code>Validators.required</code>&nbsp;是最简单的验证,表明指定的字段是必填项,否则就认为&nbsp;<code>FormControl</code>&nbsp;是无效的</p>
<p>如果&nbsp;<code>FormGroup</code>&nbsp;中有一个&nbsp;<code>FormControl</code>&nbsp;是无效的, 那整个&nbsp;<code>FormGroup</code>&nbsp;都是无效的</p>
<p>要为&nbsp;<code>FormControl</code>&nbsp;对象分配一个验证器 ,可以直接把它作为第二个参数传给&nbsp;<code>FormControl</code>&nbsp;的构造函数</p>
<div class="cnblogs_code">
<pre>const control = <span style="color: rgba(0, 0, 255, 1)">new</span> FormControl('name'<span style="color: rgba(0, 0, 0, 1)">, Validators.required);

</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 在组件定义类中使用 FormBuilder</span>
<span style="color: rgba(0, 0, 0, 1)">constructor(fb: FormBuilder) {
    </span><span style="color: rgba(0, 0, 255, 1)">this</span>.myForm =<span style="color: rgba(0, 0, 0, 1)"> fb.group({
      </span>'name': [''<span style="color: rgba(0, 0, 0, 1)">,Validators.required]
    });
    </span><span style="color: rgba(0, 0, 255, 1)">this</span>.name = <span style="color: rgba(0, 0, 255, 1)">this</span>.myForm.controls['name'<span style="color: rgba(0, 0, 0, 1)">];
}</span></pre>
</div>
<p>在视图中检查验证器的状态,并据此采取行动</p>
<div class="cnblogs_code">
<pre>template:`&lt;div&gt;
      &lt;h2&gt;商品表单:商品名称&lt;/h2&gt;
      &lt;form ="myForm" (ngSubmit)="onSubmit(myForm)"&gt;
      &lt;div&gt;
          &lt;label <span style="color: rgba(0, 0, 255, 1)">for</span>="nameInput"&gt;商品名称:&lt;/label&gt;
          &lt;<span style="color: rgba(0, 0, 0, 1)">input
            type</span>="text"<span style="color: rgba(0, 0, 0, 1)">
            id</span>="nameInput"<span style="color: rgba(0, 0, 0, 1)">
            placeholder</span>="请输入名称"<span style="color: rgba(0, 0, 0, 1)">
            </span>="myForm.controls['name']"
          /&gt;
          &lt;div style="color:red" *ngIf="!name.valid"&gt;<span style="color: rgba(0, 0, 0, 1)">
            名称无效
          </span>&lt;/div&gt;
          &lt;div style="color:red" *ngIf="name.hasError('textinvalid')"&gt;<span style="color: rgba(0, 0, 0, 1)">
            名称不是以“</span>123<span style="color: rgba(0, 0, 0, 1)">”开头
          </span>&lt;/div&gt;
          &lt;div *ngIf="name.dirty"&gt;<span style="color: rgba(0, 0, 0, 1)">
            数据已变动
          </span>&lt;/div&gt;
      &lt;/div&gt;
      &lt;div&gt;
          &lt;label <span style="color: rgba(0, 0, 255, 1)">for</span>="codeInput"&gt;商品料号:&lt;/label&gt;
          &lt;<span style="color: rgba(0, 0, 0, 1)">input
            type</span>="text"<span style="color: rgba(0, 0, 0, 1)">
            id</span>="codeInput"<span style="color: rgba(0, 0, 0, 1)">
            placeholder</span>="请输入料号"<span style="color: rgba(0, 0, 0, 1)">
            </span>="myForm.controls['code']"
          /&gt;
          &lt;<span style="color: rgba(0, 0, 0, 1)">div
            style</span>="color:red"
            *ngIf="myForm.controls.code.hasError('required')"
          &gt;<span style="color: rgba(0, 0, 0, 1)">
            该项必填
          </span>&lt;/div&gt;
          &lt;<span style="color: rgba(0, 0, 0, 1)">div
            style</span>="color:red"
            *ngIf="myForm.controls.code.hasError('pattern')"
          &gt;<span style="color: rgba(0, 0, 0, 1)">
            只可输入数字和英文
          </span>&lt;/div&gt;
      &lt;/div&gt;
      &lt;div style="color:green" *ngIf="myForm.isvalid"&gt;<span style="color: rgba(0, 0, 0, 1)">
          表单无效
      </span>&lt;/div&gt;
      &lt;div style="color:green" *ngIf="myForm.valid"&gt;<span style="color: rgba(0, 0, 0, 1)">
          表单有效
      </span>&lt;/div&gt;
      &lt;button type="submit"&gt;提交&lt;/button&gt;
      &lt;/form&gt;
    &lt;/div&gt;`
<span style="color: rgba(0, 0, 0, 1)">export class NonInWarehouseComponent implements OnInit {
myForm: FormGroup;
name: AbstractControl;
constructor(fb: FormBuilder) {
    </span><span style="color: rgba(0, 0, 255, 1)">this</span>.myForm =<span style="color: rgba(0, 0, 0, 1)"> fb.group({
      name: [</span>'牛奶'<span style="color: rgba(0, 0, 0, 1)">, Validators.compose()],
      code: [</span>'', *$'<span style="color: rgba(0, 0, 0, 1)">)]],
    });
    </span><span style="color: rgba(0, 0, 255, 1)">this</span>.name = <span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.myForm.controls.name;
}
ngOnInit() {
    const nameControl </span>= <span style="color: rgba(0, 0, 255, 1)">new</span> FormControl('nate'<span style="color: rgba(0, 0, 0, 1)">);
    console.log(</span>'nameControl'<span style="color: rgba(0, 0, 0, 1)">, nameControl);
}
onSubmit(a: any) {
    console.log(</span>'a'<span style="color: rgba(0, 0, 0, 1)">, a);
}
}</span></pre>
</div>
<p><strong>内置校验器</strong></p>
<p>Angular 提供了几个内置校验器,下面是比较常用的校验器:</p>
<ul>
<li><code>Validators.required</code>&nbsp;- 表单控件值非空</li>
<li><code>Validators.email</code>&nbsp;- 表单控件值的格式是 email</li>
<li><code>Validators.minLength()</code>&nbsp;- 表单控件值的最小长度</li>
<li><code>Validators.maxLength()</code>&nbsp;- 表单控件值的最大长度</li>
<li><code>Validators.pattern()</code>&nbsp;- 表单控件的值需匹配 pattern 对应的模式(正则表达式)</li>
</ul>
<p><strong>自定义验证器</strong></p>
<p>假设我们的 name 有特殊的验证需求,比如 name 必须以 123 作为开始</p>
<p>当输入值(控件的值&nbsp;<code>control.value</code>)不是以 123 作为开始时,验证器会返回错误代码&nbsp;<code>invalidSku</code></p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> angular 源代码中实现 Validators.required </span>
<span style="color: rgba(0, 0, 0, 1)">export class Validators {
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 接收一个 AbstractControl 对象作为输入</span>
    static required(control: AbstractControl): ValidationErrors | <span style="color: rgba(0, 0, 255, 1)">null</span><span style="color: rgba(0, 0, 0, 1)">;
}
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 当验证器失败时,会返回一个 String Map&lt;string,any&gt; 对象,他的键是” 错误代码 “,它的值是 true</span>
export declare type ValidationErrors =<span style="color: rgba(0, 0, 0, 1)"> {
    : any;
};

</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 自定义验证器</span>
<span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> textValidator(
controls: FormControl </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 因为FormControl继承于 AbstractControl 所以也可以写成FormControl对象</span>
<span style="color: rgba(0, 0, 0, 1)">): {
: </span><span style="color: rgba(0, 0, 255, 1)">boolean</span><span style="color: rgba(0, 0, 0, 1)">;
} {
</span><span style="color: rgba(0, 0, 255, 1)">if</span> (!controls.value.match(/^123/<span style="color: rgba(0, 0, 0, 1)">)) {
    </span><span style="color: rgba(0, 0, 255, 1)">return</span> { textinvalid: <span style="color: rgba(0, 0, 255, 1)">true</span><span style="color: rgba(0, 0, 0, 1)"> };
}
}</span></pre>
</div>
<p>给&nbsp;<code>FormControl</code>&nbsp;分配验证器,但是 name 已经有一个验证器了,如何在同一个字段上添加多个验证器</p>
<p>用&nbsp;<code>Validators.compose</code>&nbsp;来实现</p>
<p><code>Validators.compose</code>&nbsp;把两个验证器包装在一起,我们可以将其赋值给&nbsp;<code>FormControl</code></p>
<p>只有当两个验证器都合法时,<code>FormControl</code>&nbsp;才是合法的</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 0, 1)">Validators.compose()
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 不用compose   </span><span style="color: rgba(0, 128, 0, 1)">
//</span><span style="color: rgba(0, 128, 0, 1)"> 保留 compose 是为了向以前历史版本进行兼容,不用 compose 也可实现</span></pre>
</div>
<h3><strong>动态表单</strong></h3>
<p>要实现 Angular 动态表单,主要使用&nbsp;<code>formArray</code>&nbsp;方法,<code>formArray</code>&nbsp;生成的实例是一个数组,在这个数组中可以动态的放入&nbsp;<code>formGroup</code>&nbsp;和&nbsp;<code>formControl</code>,这样便形成了动态表单。</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 0, 1)">export class ReativeFormsComponent implements OnInit {
ngOnInit() {
    </span><span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.addContact()
}
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">动态表单</span>
personMess: FormGroup = <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> FormGroup({
    </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">生成动态表单数组</span>
    contacts: <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> FormArray([])
})
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">获取数组对象</span>
<span style="color: rgba(0, 0, 0, 1)">get contacts(){
    </span><span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 0, 255, 1)">this</span>.personMess.get('contacts'<span style="color: rgba(0, 0, 0, 1)">) as FormArray
}
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">增加一个表单组</span>
<span style="color: rgba(0, 0, 0, 1)">addContact(){
    let myContact </span>= <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> FormGroup({
      name: </span><span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> FormControl(),
      phone: </span><span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> FormControl()
    })
    </span><span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.contacts.push(myContact)
}
   </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">删除一个表单组</span>
<span style="color: rgba(0, 0, 0, 1)">deleteContact(i:number){
    </span><span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.contacts.removeAt(i)
}
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">提交表单</span>
<span style="color: rgba(0, 0, 0, 1)">OnSubmit() {
    console.log(</span><span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.personMess.value)
}
}
</span>&lt;form ="personMess" (submit)="OnSubmit()"&gt;
&lt;div formArrayName="contacts"&gt;
    &lt;!-- 注意:这里遍历的时contacts.controls --&gt;
    &lt;div *ngFor="let contact of contacts.controls;let i =index" ="i"&gt;
      &lt;input type="text" formControlName="name"&gt;
      &lt;input type="text" formControlName="phone"&gt;
      &lt;button (click)="deleteContact(i)"&gt;删除信息&lt;/button&gt;
    &lt;/div&gt;
&lt;/div&gt;
&lt;button (click)="addContact()"&gt;添加信息&lt;/button&gt;&lt;br&gt;
&lt;input type="submit"&gt;
&lt;/form&gt;</pre>
</div>
<h2><strong>Angular CDK</strong></h2>
<p>CDK 是&nbsp;<code>Component Dev kit</code>&nbsp;的简称,是 Angular Material 团队在开发 Library 时发现组件有很多相似的地方,最后进行了抽取,提炼出了公共的逻辑,这部分即是 CDK</p>
<p>官方用了一个很形象的比喻:如果组件库是火箭飞船,那么 CDK 就是发动机零件盒<br><img src="https://img.php.cn/upload/article/000/000/024/813ac9748951760246e3aa9213c016f1-9.png"></p>
<p>更多编程相关知识,请访问:编程入门!!</p><br><br>
来源:https://www.cnblogs.com/moiom/p/16963973.html
頁: [1]
查看完整版本: Angular新手入门指南【最全】