• angular快速上手


    文章目录


    前言

    Angular学习

    课程链接


    一、模块(一组功能的集合)

    1. angular内部是typescript编写的 内部是模块化开发
    2. 可以使用angular中的 CLI方式创建 简便
    3. 在angular中有很多的ngModule 这样去组织代码的 也就是模块
    4. 一个ngModule可以使用另一个ngModule导出的功能 并不是相互独立的

    二、组件(是用来描述用户界面的)

    1. 组件类 组件模板 组件样式
    组件类:component.ts 
    组件模板:component.html 
    组件样式:component.scss
    
    • 1
    • 2
    • 3
    1. 组件必须要属于一个ngModule的
    2. 同一个组件不能同时属于多个模块的 要想使用了呢?
    其它模块依赖当前的模块 前提 当前模块要导出该组件
    
    • 1

    三、服务(为了解耦组件类中的代码)

    场景:组件和组件之间可以共享的数据 angular不希望都写一遍 可以放置在服务中
    服务类的实例对象 由angular帮忙创建 直接在组件类中通过construct的形参的方式
    服务是单例模式


    四、共享模块(是一个ngMoudle)

    1. 首先创建一个共享模块以及共享组件并导出
    2. 其次在根模块导入共享模块
    3. 在根组件中使用共享模块的组件

    注意:将组件放在哪个模块的文件夹下 CLI会自动地识别 组件是属于哪一个module

    数据绑定:

    就是将组件类中的数据显示在组件模板中
    即组件类和组件模板进行绑定
    数据驱动DOM

    组件中可以套用组件


    五、组件模板

    1. 数据绑定

    差值表达式的使用 在差值表达式中不能够写if else的 显示字符串时 需要加上单引号

    <!-- 数据展示 -->
    <div>
      {{ message }}
    </div>
    <div>
      {{ getInfo() }}
    </div>
    <div [innerHTML]="htmlString"></div>
    <div>{{ 1 === 1 ? '相等' : '不相等' }}</div>
    <div>{{ 'Angular' }}</div>```
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    1. 属性绑定

    DOM对象属性绑定 HTML标记属性和DOM都有 使用DOM对象属性绑定=“”
    HTML标记属性绑定 attr.绑定的是自己的标记属性 DOM中没有的 不加这个字段会报错

    <!-- 属性绑定的两种形式 -->
    <!-- 1 HTML标记属性绑定 -->
    <div [attr.data-test]="title"></div>
    
    • 1
    • 2
    • 3

    六、动态为元素添加类名及行内样式

    绑定类名:

    1. [class.添加的类名]=“boolean”
    2. [ngClass]=“{类名:”“boolean,…}”
    <!-- 为元素添加类名 不会改变原有的类 -->
    <div class="a" [class.active]="true"></div>
    <div class="b" [ngClass]="{ active: true, error: true }"></div>
    
    行内样式和类名 相似
    <!-- 行内样式 -->
    <div [style.width]="'200px'"></div>
    <div
      [ngStyle]="{ width: '200px', height: '200px', backgroundColor: 'red' }"
    
    ></div>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    七、事件绑定

    为元素绑定事件

    <button (click)="onClick($event)">button</button>
    <input type="text" (keyup.enter)="onKeyup()" />
    
    • 1
    • 2

    八、获取原生DOM对象

    1. 在组件模板中获取 使用模板变量
    2. 在组件类中获取 ViewChild获取一个元素 ViewChildren获取一组元素

    模板变量 ?. 对于程序的一种保护 当后者不存在时 便不会去访问


    九、内容投影

    1. 根组件中只有一个div或者其他元素时 只需要在另一个组件中写一个ng-content进行占位即可
    2. 当有多个时 需要在ng-content指定select =“.类名”
      小知识:如果不想要外面的div 可以直接在根组件中使用ng-container

    十、双向数据绑定

    1. 表单模块提供的–名字是formsMoudle
    2. 在input中使用[(ngModel)]去绑定组件类中的某一个属性

    十一、数据绑定容错处理

    1. 方式一:直接通过ngIf去判断 存在的时候 再往下取值
    2. 方式二:使用?.

    十二、引入全局样式的三种方式

    1. 在style.css文件中

    @import “路径”

    1. 在index.html文件中
    2. angular.json文件

    十三、指令(在angular中去操作DOM)

    1. 属性指令:修改现有元素的外观或行为 使用[]包围

    2. 结构指令:增加 或者删除 DOM节点以修改布局 使用*作为指令前缀

    内置指令:

    《1》*ngIf 根据条件渲染DOM节点或者移除DOM节点 值是boolean 《2》[hidden]
    《3》*ngFor 遍历数据生成HTML结构


    十四、自定义指令

    通过自定义指令来操作DOM
    创建自定义指令:ng g d 指令的路径以及名字


    十五、angular管道

    pipe 管道的作用是格式化组件模板数据

    angular内置常用管道:

     // pipe的使用
      date = new Date();
      money = 33333;
    
      test: {
        person: {
          name: '张三';
          age: 20;
        };
      };
    <!-- pipe使用 -->
    <br />
    <br />
    <div>{{ date | date: 'YYYY-MM-dd' }}</div>
    <div>{{ money | currency: '¥' }}</div>
    <div>{{ message | uppercase }}</div>
    <br />
    <br />
    <br />
    <div>
      <pre> {{ test | json }}</pre>
    </div>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    //自定义管道
    ng g p
    
    • 1
    • 2

    十六、组件通讯

    1. 向组件内部传递数据 name age

    使用装饰器
    @Input(“name”) myName : string = “”
    @Input(“age”) myAge : string = “”

    1. 组件向外部传递数据(将子组件中的数据传递给父组件)
    <app-person></app-person>
    
    • 1

    十七、angular中的组件生命周期函数

    挂载 更新 卸载

    《1》挂载阶段:

    a:
    constructor
    angular在实例化组件类时执行 可以用来接受angular注入的服务实例对象
    b:
    ngOnInit
    在首次接收到输入属性值后执行 在此处可以执行请求操作
    //从组件外部传入到组件内部的
    c:
    ngAfterContentInit
    当内容投影初始渲染完成后调用
    d:
    ngAfterViewInit
    当组件视图渲染完成后调用
    //代码解释
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    《2》更新阶段:

    ngOnChanges
    a:
    当输入属性值发生变化时执行,初始设置时也会执行一次,顺序优于ngOninit
    b:
    不论多少输入属性同时变化,钩子函数只会执行一次,变化的值会同时存储在参数中
    c:
    参数类型为SimpleChanges,
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    《3》卸载阶段:

    ngOnDestroy
    当组件被销毁之前调用,用于清理操作
    组件之间相互切换 A切换到B A被销毁
    export class HomeComponent implements OnDestroy {
    	ngOnDestro(){
    		consle.log("组件被卸载")
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    十八、依赖注入

    解决代码之间耦合度高的问题

    angular中的DI框架(依赖注入框架)
    《1》Dependency 组件要依赖的实例对象 即服务实例对象
    《2》Token 获取服务实例对象的标识
    《3》Injector 注入器 负责创建维护服务类的实例对象并向组件中注入服务实例对象
    《4》Provider 配置注入器的对象 指定创建服务实例对象的服务类和获取实例对象的标识

    使用:
    《1》从@angular/core中引入 ReflectiveInjector
    《2》创建注入器对象 injector = ReflectiveInjector.resolveAndCreate()
    《3》通过注入器对象的get方法来获得实例对象

    服务实例对象为单例模式 注入器在创建服务实例后会对其进行缓存

    不同的注入器返回不同的服务实例对象

    父级下面的字级注入器

    服务实例的查找类似函数作用域链 当前级别可以找到就使用当前的级别 当前级别找不到去父级中查找


    十九、provider的使用

    1. 配置注入器的对象 指定了创建实例对象的服务类和访问服务实例对象的标识
    const injector = ReflectiveInjector.resolveAndCreate([
    { provide: MailService , useClass : MailService }
    })
    //provide 获取实例对象的唯一标识
    
    • 1
    • 2
    • 3
    • 4
    1. 访问依赖对象的标识也可以是字符串类型
    const injector = ReflectiveInjector.resolveAndCreate({
    { provide: mail , useClass : MailService }
    })
    const mailService  = injector.get("mail")
    //好处:将实例对象和外部的引用建立了松耦合关系 外部通过标识获取实例对象 只要标识/字符串 保持不变 内部代码怎么变都不会影响到外部
    
    • 1
    • 2
    • 3
    • 4
    • 5
    1 使用注入器存储实例对象 
    《2 对象
    const injector = ReflectiveInjector.resolveAndCreate({
    {
    provide : "Config",
    useValue: Object.freeze({
    APIKEY:""
    APISCRET:""
    })
    }
    ])
    const Config = injector.get("Config")
    //freeze 只是允许访问 不允许更改
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    二十、服务的创建与注入

    //创建服务:
    import {Injectable} from '@angular/core'
    
    @Injectable({
    provideIn:'root'
    })
    
    export class TestService { }
    
    export class AppComponent {
     constructor (private testService : TestService) { }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    二十一、服务的作用域

    使用服务可以轻松实现跨模块跨组件共享数据
    三种级别的注入器 放置的位置不同 作用的范围是不同的

    《1》 根注入器

    import {Injectable} from '@angular/core';
    
    @Injectable({
    provideIn:'root'
    })
    export class CardListService { }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    《2》模块级别注入器

    import {Injectable} from '@angular/core';
    import {CarModule} from './car.module';
    
    @Injectable({
    provideIn:'CarModule'
    }),
    
    export class CardListService { }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    两种实现的形式

    import {CardListService} from './car-list.service';
    
    @NgModule({
    providers : [CarListService],
    }
    export class CarModule {}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    《3》组件级别注入器

    在组件级别注册服务 该组件及其子组件使用同一个服务实例对象

    import {Component} from '@angular/core';
    import {CarListService} from '../car-list.service.ts';
    
    @Component({
    selector : 'app-car-list',
    templateUrl:'./car-list.component.html',
    providers: [CarListService]
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    二十二、模板驱动表单用法

    两种方式:

    《1》模板驱动表单

    表单的逻辑写在模板中 适合简单的表单类型

    1. 引入依赖模块FormsMoudle
    import {FormsModule} from "@angular/forms"
    
    @NgModule({
    imports : [FormsModule],
    )}
    export class AppModule{}
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    1. 将DOM表单转换为ngForm(方便使用一些angular中的一些方法)
    <form #f="ngForm" (submit) = "onSubmit(f)"></form>
    
    • 1
    1. 声明表单字段为ngModel
    <form #f="ngForm" (submit) = "onSubmit(f)">
    <input type = "text" name="username" ngModel/>
    <button>提交</button>
    </form>
    
    • 1
    • 2
    • 3
    • 4
    1. 获取表单字段的值
    import {ngForm} from "@angular/forms"
    
    export class AppComponent {
    onSubmit(form: NgForm){
    	console.log(form.value)
    }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    1. 表单分组
      将有关联的表单项存储在一个对象中
    <form #f="ngForm" (submit)="onSubmit(f)">
      <div ngModelGroup="user"><input type="text" name="username" ngModel /></div>
      <div ngModelGroup="contact"><input type="text" name="phone" ngModel /></div>
      <button>提交</button>
    </form>
      onSubmit(form: NgForm) {
        console.log(form.value.user.username);
        console.log(form.value.contact.phone);
      }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    表单验证

    required 必填字段
    minlength 字段最小长度
    maxlength 字段最大长度
    pattern 验证正则 例如:pattern=“\d” 匹配一个数值

    <form #f="ngForm" (submit) = "onSubmit(f)">
    <input type="text" name="username" ngModel required pattern="\d"/>
    <button>提交</button>
    </form>
    
    export class AppComponent{
    	onSubmit(form:NgForm){
    	console.log(form.valid)
    }
    }
    //表单整体未通过验证时禁用提交按钮
    <button type="submit" [disable]="f.invalid">提交</button>
    //在组件模板中显示表单项未通过时的错误信息
    <form #f="ngForm" (submit) = "onSubmit(f)">
    <input #username="ngModel"/>
    <div *ngIf = "username.touched && !username.valid && username.errors">
    <div *ngIf="username.errors.required">请填写用户名</div>
    <div *ngIf = "username.errors.pattern">不符合正则规则</div>
    </div>
    </form>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    指定表单项未通过时验证的样式

    input.ng-touched.ng-invalid{
    border : 2px solid red;
    }
    
    • 1
    • 2
    • 3

    //最大的字符输入校验 新版本的angular已经支持 不需要校验

    字符数目也可以动态获取

    《2》模型驱动表单

    <1>表单的逻辑写在组件类中,对验证逻辑拥有更多的控制权 适合复杂的表单的类型

    <2>在模型驱动表单中 表单字段需要是FormControl类的实例 实例对象可以验证表单字段中的值 值是否被修改过等等

    <3>一组表单字段构成整个表单 整个表单需要是FormGroup类的实例 它可以对表单进行整体验证

    1. FormControl:表单组中的一个表单项
    2. FormGroup:表单组,表单至少是一个FormGroup
    3. FormArray:用于复杂表单,可以动态添加表单项或表单组,在表单验证时,FormArray中有一项没有通过 整体不通过

    //前面是概念 下面是一些用法
    《1》引入ReactiveFormsModle

    import {ReactiveFormsModule} from "@angular/forms"
    
    @NgModule({
    imports:{ReactiveFormsModule}
    })
     export class AppModule{}
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    《2》在组件类中创建FormGroup表单控制对象

    import {ReactiveFormsModule} from "@angular/forms"
     export class AppComponent{
    contactForm : FormGroup = new FormGroup({
    name: new FormControl(),
    phone : new FormControl()
    }}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    《3》关联组件模板中的表单

    <form [formGroup] = "contactForm" (submit)="onSubmit()">
    <input type="text" formControlName="name"/>
    <input type="text" formControlName="phone"/>
    <button>提交</button>
    </form>
    
    • 1
    • 2
    • 3
    • 4
    • 5

    《4》获取表单的值

    export class AppComponent{
    onSubmit(){
    console.log(this.contactForm.value)
    }
    
    • 1
    • 2
    • 3
    • 4

    《5》设置表单默认值

    contactForm : FormGroup = new FormGroup({
    name: new FormControl("默认值"),
    phone : new FormControl(12345678910)
    }
    • 1
    • 2
    • 3
    • 4

    《6》表单分组

    contactForm : FormGroup = new FormGroup({
    fullName: new FormGroup({
     firstName: new FormControl(),
    lastName : new FormControl()
    }),
    phone : new FormControl()
    }<form [formGroup] = "contactForm" (submit)="onSubmit()">
    <div formGroupName="fullName">
    <input type="text" formControlName="firstName"/>
    <input type="text" formControlName="lastName"/>
    </div>
    <input type="text" formControlName="phone"/>
    <button>提交</button>
    </form>
    
    onSubmit(){
    console.log(this.contactForm.value.fullName.firstName)
    console.log(this.contactForm.get(["fullName","firstName"])?.value)
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    二十三、使用formArray动态创建表单

    需求:在页面中默认显示一组联系方式,通过点击按钮可以添加更多联系方式组

    一步步去绑定

    // 经典


    二十四、内置表单验证器(模型驱动)

    《1》使用内置验证器提供的验证规则验证表单字段

    import {FormControl,FormGroup,Validators } from "@angular/forms"
    
    contactForm : FormGroup = new FormGroup({
    name : new FormControl("默认值",[
    Validators.required,
    Validators.minLength(2)
    ])
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    《2》获取整体表单是否通过

    onSubmit(){
    console.log(this.contactForm.valid)
    }
    //表单整体验证未通过时禁用表单按钮
    <button [disabled] = "contactForm.invalid">提交</button>
    
    • 1
    • 2
    • 3
    • 4
    • 5

    《3》在组件模板中显示未验证通过时的错误信息

    get name (){
    return this.contactForm.get("name")
    }
    //在组件类中 添加了一个name属性 用于获取name
    和模板驱动类似
    
    • 1
    • 2
    • 3
    • 4
    • 5

    自定义同步表单验证器(模型驱动)

    《1》自定义验证器的类型是TypeScript类——新建一个ts文件 写类
    《2》类中包含的具体方法 验证方法必须为静态方法
    《3》验证方法中有一个参数control 类型为AbstractControl 其实就是FormControl类的实例对象的类型
    《4》如果验证成功,返回null
    《5》如果验证失败 返回对象 对象中的属性即为验证标识 值为true 标识该项验证失败
    《6》验证方法的返回值为ValidationErrors | null

    import {AbstractControl,ValidationErrors} from "angular/forms"
    
    export class NameValidators {
    static cannotContaomSpace (control:AbstractControl) : ValidationErrors | null{
    //验证未通过
    if(/\s/.test(control.value))  return {cannotContaomSpace:true}
    //验证通过
    return null
    }
    }
    
    import {NameValidators} from "./Name.validators"
    
    contactForm: FormGroup = new FormGroup({
    name : new FormControl("",[
    Validators.required,
    NameValidators.cannotContaomSpace
    ])
    })
    
    <div *ngIf="name.touched &&name.invalid &&name.errors"></div>
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    自定义异步表单验证器(模型驱动)
    当用户在文本框输入了内容 离开焦点时 看输入的内容是否是唯一的

    //比如说可以向服务器端发送请求来证实这个事情
    static shouldBeUnique(
    control: AbstractControl
    ):Promise<ValidationErrors | null>{
    return new Promise(function (resolve)){
    setTimeout(function(){
    if(control.value === "admin"){
    resolve({shouldBeUnique:true})
    }else{
    resolve(null)
    }
    },2000)
    }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    在name内的数组写的验证规则都是同步的

    name : new FormControl("",[
    Validators.required,
    NameValidators.cannotContaomSpace
    ])
    
    • 1
    • 2
    • 3
    • 4

    还可以加第三个参数

    name : new FormControl("",[
    Validators.required,
    NameValidators.cannotContaomSpace
    ],NameValidators.shouldBeUnique)
    
    • 1
    • 2
    • 3
    • 4

    在验证的过程中给用户一些提示 显示一些状态信息
    在验证的过程中会有一个字段pending 为true

    <div *ngIf="name.errors.shouldBeUnique">名字已经存在</div>
    <div *ngIf="name.pending">正在验证...</div>
    
    • 1
    • 2

    二十五、FormBuilder

    创建表单的快捷方式

    1. this.fb.control:表单项
    2. this.fb.group:表单组 表单至少是一个FormGroup
    3. this.fb.array: 用于复杂表单 可以动态添加表单项或表单组 在表单验证时,FormArray中有一项没通过 整体没通过
    import {FormBuilder ,FormGroup, Validators} from "@angular/forms"
    
    export class AppComponent{
    	contactFrom : FormGroup
    	constructor(private fb : FormBuilder){
    	  this.contactForm = this.fb.group({
    	     fullName: this.fb.group({
    		firstName:["",[Validators.required]],
    		lastName: [""]
    }),
    phone:[]
    })
    }
    }
    
    //可以将group和array中的内容给替换掉 数组是一种简写的形式
    //表单组里面包含表单项
    this.fb.group
    this.fb.array
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    组件模板:

    <form [formGroup]="contactForm" (submit)="onSubmit()">
    <div formGroupName="fullName">
    	<input type="text" formControlName="firstName"/>
    	<input type="text" formControlName="lastName"/>
    </div>
    <button>提交</button>
    </form>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    //练习

    1. 获取一组复选框中选中的值
    <form [formGroup]="form" (submit)="onSubmit()">
    	<label *ngFor="let item of Data">
    	   <input type="checkbox" [value]="item.value" (change)="onChange($event)"/>
    		{{item.name}}
    	</label>
    <button>提交</button>
    </form>
    
    import {Component} from "@angular/core"
    import {FormArray,FormBuilder,FormGroup} from "@angular/forms"
    interface Data{
    	name:string
    	value:string
    }
    @Component({
    selector :"app-checkbox",
    templateUrl:"./checkbox.component.html",
    styles:[]
    })
    export class CheckboxComponent{
      Data : Array<Data>=[
         {name:"Pear",value:"pear"},
         {name:"Plum",value:"plum"},
         {name:"Kiwi",value:"kiwi"},
         {name:"Apple",value:"apple"},
         {name:"Lime",value:"lime"}
    ]
    form:FormGroup
    constructor(private fb: FormBuilder){
    this.form = this.fb.group({
    checkArray : this.fb.array([])
    })
    } 
    }
    
    //name存储的是给用户看的数据 
    //value存储的是在提交表单时 需要获取到的数据
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    1. 提交表单时 获取用户选择的性别
      在组件类中
    export class RadioComponent implements OnInit{
      form:FormGroup = new FormGroup({
    	gender :  new FormControl()
    })
    
    onSubmit(){
       console.log(this.form,value)
    }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    在组件模板中

    <form [formGroup]="form" (submit)="onSubmit()">
    <input type="radio" value="male" formControlName="gender">male
    <input type="radio" value="female" formControlName="gender">female
    <button>提交</button>
    </form>
    
    • 1
    • 2
    • 3
    • 4
    • 5

    模型驱动表单 常用方法:

    《1》patchValue:设置表单控件的值(可以设置全部,也可以设置其中某一个 其他不受影响)
    《2》setValue:设置表单控件的值(设置全部 不能排除任何一个)
    《3》valueChanges:当表单控件的值发生变化时被触发的事件
    《4》reset 表单内容置空


    二十六、路由的基本使用

    在angular中 路由是以模块为单位的 每个模块都可以有自己的路由

    快速上手:

    1. 创建页面组件 Layout组件以及Navigation组件 供路由使用

    《1》创建首页页面组件 ng g c pages/home
    《2》创建关于我们页面组件 ng g c pages/about
    《3》创建布局组件 ng g c pages/layout
    《4》创建导航组件 nng g c pages/navigation

    1. 创建路由规则
    app.module.ts
    import {Routes} from "@angular/routers"
    
    const routes:Routes = [
    {
    path:"home",
    component:HomeComponent
    },
    {
    path:"about",
    component:AboutComponent
    }
    ]
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    1. 引入路由模块并启动
    //app.module.ts
    import {RouterModule,Routes} from "@angular/router"
    
    //用来启动路由
    @NgModule({
    imports: [RouterModule.forRoot(routes,{useHash:true})],
    })
    export class AppModule{}
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    1. 添加路由插座
    //路由插座即为占位组件 匹配到的路由组件将会显示在这个地方
    <router-outlet></router-outlet>
    
    • 1
    • 2
    1. 在导航组件中定义链接
    <a routerLink="/home">首页</a>
    <a routerLink="/about">关于我们</a>
    
    • 1
    • 2

    匹配规则:
    {
    path:“”,
    //重定向
    redirectTo:“home”,
    //完全匹配斜杠时 才重定向
    pathMatch:“full”
    }

    路由默认是从上到下进行匹配的 什么都没有匹配上
    404页面:
    {
    path:“**”,
    component:NotFoundComponent
    }


    二十七、路由传递参数的两种形式

    《1》查询参数

    <a routerLink="/about" [queryParams]="{name:'Kitty'}">关于我们</a>
    
    import {ActivateRoute} from "@angular/router"
    
    export class AboutComponent implements OnInit{
      constructor(private route:ActivateRoute){}
       
    ngOnInit(): void{
    this.route.queryParaMap.subscribe(query=>{
    query.get("name")
    })
    }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    《2》动态参数

    const routes:Routes = [
    {
    path:"home",
    component:HomeComponent
    },
    {
    path:"about/:name",
    component:AboutComponent
    }
    ]
    
    <a [routerLink]="['/about','zhangsan']">关于我们</a>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    方法变成:

    this.route.paramMap.subscribe(query=>{
    query.get("name")
    })
    
    • 1
    • 2
    • 3

    二十八、路由嵌套(定义子孙级路由)

    指的是如何定义子级路由

    const routes:Routes = [
    {
     path:"about",
    component:AboutComponent,
    children:[
    {
    path:"introduce",
    component:IntroduceComponent
    },
    {
    path:"history",
    component:HistoryComponent
    }
    ]
    }
    ]
    
    //about.component.html
    <app-layout>
    <p>about works!</p>
    <a routerLinik="/about/introduce">公司简介</a>
    <a routerLink="/about/history">发展历史</a>
    <div>
    	<router-outlet></router-outlet>
    </div>
    <app-layout>
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    二十九、路由命名插座

    //新需求 在进入news这个一级路由时 同时显示两个二级的内容 并且
    将子级路由组件显示到不同的路由插座中
    {
     path:"about",
    component:AboutComponent,
    children:[
    {
    path:"introduce",
    component:IntroduceComponent,
    outlet:"left"
    },
    {
    path:"history",
    component:HistoryComponent,
    outlet:"right"
    }
    ]
    }
    //about.component.html
    <app-layout>
    <p>about works!</p>
    <div>
    <router-outlet name="left"></router-outlet>
    <router-outlet name="right"></router-outlet>
    </div>
    <app-layout>
    
    <a
    [routerLink]="[
    'about',{
    outlets:{
     left :['introduce'],
    right:['history']
    }
    }
    ]"
    >
    关于我们
    </a>
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40

    三十、导航路由(如何实现javascript的方法去实现页面之间的跳转)

    //app.component.html
    <button (click)="jump">跳转到发展历史</button>
    
    //app.component.ts
    import {Router} from "@angular/router"
    
    export class HomeComponent{
      constructor(private router: Router){}
      jump(){
        this.router.navigate(["/about/history"],{
           queryParams:{
    	name: "Kitty"
    }
    })
    }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    三十一、路由模块

    创建路由模块独立路由规则
    之前将所有的路由规则都放在了根模块中
    这并不是一个值得推荐的做法 需要的是单独创建一个模块,然后再该模块编写相应的路由规则 之后让根模块去引入路由模块

    在控制台创建一个模块,和app-module配合使用

    ng g m appRouting --flat=true
    
    • 1

    三十二、实现路由模块懒加载(按需加载)

    用户在首次使用应用时 只是去请求根模块,其他的模块等用户访问的时候再去加载
    路由懒加载是以模块为单位的
    《1》创建用户模块 ng g m user --routing=true ——并创建该模块的路由模块
    《2》创建登录页面组件 ng g c user/pages/login
    《3》创建注册页面组件 ng g c user/pages/register
    《4》配置用户模块的路由规则

    import {NgModule} from "@angular/core"
    import {Routes,RouterModule} from "@angular/router"
    import {LoginComponent} from "./pages/login/login.component"
    import {RegisterComponent} from "./pages/register/register.component"
    
    const  routes Routes = [
    {
    path : "login",
    component:LoginComponent
    },
    {
    path:"register",
    component:RegisterComponent
    }
    ]
    
    @NgModule({
    imports : [RouterModule.forChild(routes)],
    exports : [RouterModule]
    })
    
    export class UserRoutingModule{}
    
    //根模块启动路由 RouterModule.forRoot
    //其他模块启用路由 RouterModule.forChild
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    《5》将用户路由模块关联到主路由模块

    //app-routing.module.ts
    const routes : Routes = [
    {
    path : "user",
    loadChildren:() =>import("./user/user.module").then(m=>m.UserModule)
    }
    ]
    //loadChildren进行懒加载
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    《6》在导航组件中添加访问来链接

    <a routerLink="/user/login">登录</a>
    <a routerLink="/user/register">注册</a>
    
    • 1
    • 2

    三十三、路由守卫 CanActivate(用来保护路由组件)

    路由守卫会告诉路由是否允许导航到请求的路由
    
    路由守卫方法可以返回boolean或Observable<boolean>或Promise<boolean>它们在将来的某个时间点解析为布尔值
    
    CanActivate
    检查用户是否可以访问某一个路由
    CanActivate为接口 路由守卫类要实现该接口 该接口规定类中需要有canActivate方法 方法决定是否允许访问目标路由
    
    路由可以应用多个守卫 所有守卫方法都允许 路由才被允许访问 有一个守卫方法不允许 则路由不允许被访问
    
    创建路由守卫 ng g guard guards/auth
    //跳转
    return this.router.createUrlTree(["/home"])
    //允许
    return true
    //不允许
    return false
    //保存 需要跳转到的目标信息
    route : ActivatedRouteSnapshot 
    //
    state : RouterStateSnapshot
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    三十四、路由守卫CanActiveChild

    检查用户是否可以访问某个子路由
    创建路由守卫 ng g guard guards/admin 选择CanActiveChild
    需要将箭头移动到这个选项并且敲击空格确认选择
    
    在父级身上进行绑定
    path:"about",
    component: AboutComponent
    canActiveChild: [AdminGuard]
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    三十五、CanDeactivate

    检查用户是否可以退出路由 比如用户在表单中输入的内容没有保存 用户又要离开路由 此时可以调用该守卫提示用户


    三十六、路由守卫 Resolve

    允许在进入路由之前先获取数据,待数据获取完成之后再进入路由

    ng g resolver <name>
    
    • 1

    最直接的感受就是 当网络比较拥挤时,先获取内容在进入 防止进去出现空白界面 用户看到的是空白 认为是出bug;


    三十七、RXJS

    是一个基于异步编程的JavaScript库 目标是使编写异步和基于回调的代码更容易
    
    Angular深度集成了TypeScript 和RxJs
    服务 表单 事件 全局状态管理 异步请求 ...
    
    import {Observable} from "rxjs"
    
    const observable = new Observable(function (observer){
    	setTimeout(function(){
    		observer.next({
    	name:"张三"
    })
    },2000)
    })
    
    const observer = {
    	next : function (value){
    	console.log(value)
    }
    }
    
    observable.subscribe(observer)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    三十八、可观察对象

    《1》在Observable对象内部可以多次调用next方法向外发送数据

    import { Observable } from 'rxjs';
    
    const observable = new Observable(function (observer) {
      let index = 0;
      setInterval(function () {
        observer.next(index++);
      }, 1000);
    });
    
    const observer = {
      next: function (value) {
        console.log(value);
      },
    };
    
    observable.subscribe(observer);
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    《2》当所有数据发送完成之后,可以调用complete方法终止数据发送

    import { Observable } from 'rxjs';
    
    const observable = new Observable(function (observer) {
      let index = 0;
      let timer = setInterval(function () {
        observer.next(index++);
        if (index == 3) {
          observer.complete();
          clearInterval(timer);
        }
      }, 1000);
    });
    
    const observer = {
      next: function (value) {
        console.log(value);
      },
      complete: function () {
        console.log('终止了');
      },
    };
    
    observable.subscribe(observer);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    《3》当Observable内部逻辑发生错误时 可以调用error方法将失败信息发送给订阅者 Observable终止

    import { Observable } from 'rxjs';
    
    const observable = new Observable(function (observer) {
      let index = 0;
      let timer = setInterval(function () {
        observer.next(index++);
        if (index == 3) {
          // observer.complete();
          console.log('错误信息');
          clearInterval(timer);
        }
      }, 1000);
    });
    
    const observer = {
      next: function (value) {
        console.log(value);
      },
      complete: function () {
        console.log('终止了');
      },
      error: function (error) {
        console.log(error);
      },
    };
    
    observable.subscribe(observer);
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    说明 当调用complete或者error方法后是不能够再调用next方法

    《4》可观察对象是惰性的 只有被订阅后才会执行

    //observable.subscribe(observer);
    
    • 1

    《5》可观察对象可以有n多个订阅者observer 每次被订阅时都会得到执行


    三十九、使用Subject构造函数创建可观察对象(在订阅后不会立即执行广播)

    用于创建空的可观察对象 在订阅后不会立即执行 next方法可以在可观察对象外部调用

    import { Observable, Subject } from 'rxjs';
    
    const demoSubject = new Subject();
    
    demoSubject.subscribe({
      next: function (value) {
        console.log(value);
      },
    });
    
    demoSubject.subscribe({
      next: function (value) {
        console.log(value);
      },
    });
    
    setTimeout(function () {
      demoSubject.next('hahaha');
    }, 3000);
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    四十、使用BehaviorSubject创建可观察对象(在被订阅后可以立即执行)

    拥有Subject的全部功能 但是在创建Observable对象时 可以传入默认值 观察者订阅后可以直接拿到默认值

    import { BehaviorSubject } from 'rxjs';
    
    const demoSubject = new BehaviorSubject('默认值');
    demoSubject.subscribe({
      next: function (value) {
        console.log(value);
      },
    });
    demoSubject.next('显示');
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    四十一、ReplaySubject

    功能类似Subject 但是有新订阅者时两者处理方式不同

    Subject:不会广播历史结果
    ReplaySubject:会广播所有历史结果

    import { ReplaySubject } from 'rxjs';
    
    const rSubject = new ReplaySubject();
    
    rSubject.subscribe((value) => {
      console.log(value);
    });
    
    rSubject.next('hello 1');
    rSubject.next('hello 2');
    
    setTimeout(function () {
      rSubject.subscribe({
        next: function (value) {
          console.log(value);
        },
      });
    });
    
    hello 1
    hello 2
    hello 1
    hello 2
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    四十二、数据流 操作符

    《1》数据流:从可观察对象内部输出的数据就是数据流 可观察对象内部可以向外部源源不断的输出数据
    《2》操作符:用于操作数据流 可以对数据流进行转换 过滤等操作
    
    • 1
    • 2

    辅助方法

    //调用某些方法之后 也会返回一些可观察者对象
    range(start,length) 调用方法后返回observable对象 被订阅后会发出指定范围的数值
    
    import { range } from 'rxjs';
    
    range(1, 10).subscribe((n) => console.log(n));
    方法内部并不是一次发出length个数值 而是发送了length次 每次发送一个数值 就是说内部调用了length次
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    pipe里面放置操作符
    操作符:
    map:对数据流进行转换 基于原有值进行转换
    import { map, range } from 'rxjs';
    
    range(1, 10)
      .pipe(map((n) => n * 10))
      .subscribe((n) => console.log(n));
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    四十三、辅助方法

    《1》from:返回的是可观察对象

    import { from } from 'rxjs';
    
    // from(['a', 'b', 'c']).subscribe(console.log);
    
    function p() {
      return new Promise(function (resolve) {
        setTimeout(function () {
          resolve({ a: 1 });
        }, 2000);
      });
    }
    
    from(p()).subscribe(console.log);
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    《2》forkJoin:forkJoin是Rx版本的Promise.all() 即表示等到所有的Observable都完成之后 才一次性返回值

    需求 :发出两个请求 当两个请求都发送并且拿到结果之后 再一次性地把所有的数据都拿到
    同时发送 同时拿到结果

    《3》辅助方法fromEvent和操作符pluck 辅助方法返回是一个Observable对象

    将事件转换为可观察对象 Observable

    pluck 获取对象流对象中的属性值

    《4》辅助方法interval和操作符switchMap

    interval:每隔一段时间发出一个数值 数值递增
    import { interval } from 'rxjs';
    
    interval(1000).subscribe((n) => console.log(n));
    
    switchMap
    切换可观察对象
     <button id="btn">点击</button>
    
    import { fromEvent, interval } from 'rxjs';
    import { switchMap } from 'rxjs/operators';
    
    const button = document.getElementById('btn');
    fromEvent(button, 'click')
      .pipe(switchMap((event) => interval(1000)))
      .subscribe(console.log);
    
    再次点击当前的button时 会重新发出数据流
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    四十四、操作符

    take takeWhile takeUntil
    take:获取数据流中的前几个
    import { range, take } from 'rxjs';
    
    range(1, 10).pipe(take(5)).subscribe(console.log);
    
    takeWhile: 根据条件从数据源前面开始获取
    
    import { range } from 'rxjs';
    import { takeWhile } from 'rxjs/operators';
    
    range(1, 10)
      .pipe(takeWhile((n) => n < 6))
      .subscribe(console.log);
    
    takeUntil:接收可观察对象 当可观察对象发出值时 终止主数据源
    
    import { fromEvent } from 'rxjs';
    import { takeUntil } from 'rxjs/operators';
    const button = document.getElementById('btn');
    
    fromEvent(document, 'mousemove')
      .pipe(takeUntil(fromEvent(button, 'click')))
      .subscribe(console.log);
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    四十五、操作符节流和防抖

    节流 可观察对象高频率次向外部发出数据流,通过throttleTime限制在规定时间内每次只向订阅者传递一次数据流
    import { fromEvent } from 'rxjs';
    import { throttleTime } from 'rxjs/operators';
    const button = document.getElementById('btn');
    
    fromEvent(document, 'click').pipe(throttleTime(4000)).subscribe(console.log);
    
    debounceTime
    防抖 触发高频事件 只响应最后一次
    import { fromEvent } from 'rxjs';
    import { debounceTime } from 'rxjs/operators';
    const button = document.getElementById('btn');
    
    fromEvent(document, 'click').pipe(debounceTime(1000)).subscribe(console.log);
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    四十六、辅助方法——of 方法返回的是一个Observable

    of  将参数列表作为数据流返回
    of(
      'a',
      1,
      true,
      {
        name: 'Lisi',
        age: 34,
      },
      []
    ).subscribe(console.log); 
    
    操作符——distinctUntilChanged
    检测数据源当前发出的数据流是否和上次发出的相同 如相同 跳过 不相同 发出
    of(1, 1, 2, 2, 3, 3, 4).pipe(distinctUntilChanged()).subscribe(console.log);
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    四十七、案例(元素拖拽)


    四十八、案例(搜索案例)

    用户在文本框输入内容回车后 发送到服务器中进行检索
    <input id="search" type="text" placeholder="请输入搜索内容..."/>
    
    const search = document.getElementById("search")
    
    fromEvent(search,"keyup")
    .pipe(
    debounceTime(1000),
    map(event=>event.target.value),
    distinctUntilChanged(),
    switchMap(keyword=>
    from(
     axios.get(`https://jsonplaceholder.typicode.com/posts?q=${keyword}`)
    )
    )
    ).subscribe(console.log)
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    四十九、串联请求

    先获取token 再根据token获取用户信息
    <button id="btn">获取用户信息</button>
    点击按钮后 获取用户信息
    会执行两个操作 先获取token 其次根据token获取用户信息
    
    concatMap 合并可观察对象
    import axios from "axios"
    import {from , fromEvent} from "rxjs"
    import {pluck,concatMap} from "rxjs/operators"
    
    const button = document.getElemnetById("btn")
    
    fromEvent(button,"click")
    .pipe(
    concatMap(event=>from(axios.get("http://localhost:3005/token")).pipe(pluck("data","token"))),
    concatMap(token=>from(axios.get("http://localhost:3005/userInfo")).pipe(pluck("data")))
    ).subscribe(console.log)
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    五十、HttpClientModule

    该模块用于发送Http请求 用于发送请求的方法都返回Observable对象

    《1》引入HttpClientModule模块

    //app.module.ts
    import {httpClientModule} from "@angular/common/http"
    imports:[
    httpClientModule
    ]
    
    • 1
    • 2
    • 3
    • 4
    • 5

    《2》注入HttpClient服务实例对象,用于发送请求

    //app.component.ts
    import {HttpClient} from "@angular/common/http"
    
    export class AppComponent {
     constructor(private http:HttpClient){}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    《3》发送请求

    important {HttpClient} from "@angular/common/http"
    
    export class AppComponent implements OnInit{
    	constructor(private http:HttpClient){}
    	ngOnInit(){
    		this.getUsers().subscribe(console.log)
    }
    getUsers(){
    	return this.http.get("https://jsonplaceholder.typicode.com/users")
    }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    《4》请求方法

    this.http.get(url [, options]);
    this.http.post(url,data [, options]);
    this.http.delete(url [, options]);
    this.http.put(url,data [, options])
    
    • 1
    • 2
    • 3
    • 4

    五十一、请求参数

    HttpParams类
    使用实例:
    import {HttpParams} from "@angular/common/http"
    
    let params = new HttpParams ({
    fromObject:{
    name:"zhangsan",
    age:"20"
    }
    })
    
    params = params.append("sex","male")
    
    let params = new HttpParams(
    {
    	fromString:"name=zhangsan&age=20"
    }
    )
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    五十二、在发送数据的时候 在请求头中添加字段

    请求头字段的创建需要使用HttpHeaders类 在类实例对象下面有各种操作请求头的方法

    let headers = new HttpHeaders ({test : "Hello"})	
    
    • 1

    五十三、设置响应内容

    declare type = HttpObserve = 'body' | 'response'
    //response 读取完整响应体
    //body 读取服务器端返回的数据
    
    this.http.get(
    "https://jsonplaceholder.typicode.com/users",
    {observe: "body"}
    ).subscribe(console.log)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    五十四、拦截器

    拦截器是Angular应用中全局捕获和修改HTTP请求和响应的方式。(Token,Error)
    拦截器将只拦截使用HttpClientModule模块发出的请求。

    ng g interceptor <name>
    
    • 1

    《1》请求拦截:

    @Injectable
    export class AuthInterceptor implements HttpInterceptor{
       constructor(){}
    	//拦截方法
        intercept(
    //unknown 指定请求体(body)的类型
    request:HttpRequest<unknown>
    next: HttpHandler
    //unknown 指定请求体(body)的类型
    ): Observable<HttpEvent<unknown>>{
      setHeaders:{
    	Authorization:"Bearer xxxxxxx"
    }
    })
    //通过回调函数将修改后的请求头回传给应用
    return next.handle(req)
    }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    《2》响应拦截

    @Injectable()
    export class AuthInterceptor implements HttpInterceptor{
    constructor(){}
    //拦截方法
    intercept(
    request:HttpRequest<unknown>,
    next: HttpHandler
    ): Observable<any>{
    return next.handle(request).pipe(
    retry(2),
    catchError((error: HttpErrorResponse=> throwError(error)))
    )
    }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    《3》拦截器注入

    import {AuthInterceptor} from "./auth.interceptor"
    import {HTTP_INTERCEPTORS} from "@angular/common/http"
    
    @NgModule({
    providers:[
    {
       provide:HTTP_INTERCEPTORS,
       useClass:AuthInterceptor,
       multi:true
    }
    ]
    })
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    五十五、Angular Proxy (代理)

    开发环境和接口不在同一个域中
    1.在项目的根目录下创建 proxy.conf.json文件并加入如下代码

    {
    "/api/*":{
    "target":"http://localhost:3070",
    "secure":false,
    "changeOrigin":true
    }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    《1》/api/* 在应用中发出的以/api开头的请求走此代理
    《2》target 服务器端URL
    《3》secure 如果服务器端URL的协议是https 此项需要为true
    《4》changeOrigin 如果服务端不是localhost 此项需要为true

    2.指定proxy配置文件(方式一)

    "scripts":{
    "start":"ng serve --proxy-config proxy.conf.json",
    }
    3.指定proxy配置文件(方式二)
    "serve":{
    "options":{
    "proxyConfig":"proxy.conf.json"
    },
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    五十六、NgRx

    NgRx是Angular应用中实现全局状态管理的Redux架构解决方案

    1.@ngrx/store:全局状态管理模块
    2.@ngrx/effects:处理副作用
    3.@ngrx/store-devtools:浏览器调式工具,需要依赖Redux Devtools Extension
    4.@ngrx/schematics:命令行工具,快速生成NgRx文件
    5.@ngrx/entity:提高开发者在Reducer中操作数据的效率
    6.@ngrx/router-store:将路由状态同步到全局Store
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    五十七、NgRx基本

    1》下载NgRx
    《2》配置NgRxCLI
    《3》创建Store 用来存储Reducer返回的状态
    //store 中存储的状态类型接口
    export interface AppState{}
    //状态名字和reducer的对应的关系
    export const reducers: ActionReducerMap<AppState>={}4》创建Action
    //store 中存储的状态类型接口
    //状态名字和reducer的对应的关系5》创建Reducer
    《6》创建Select——用来从Store中获取数据
    ng g selector store/selectors/counter --skipTests
    忽略测试文件
    import {createFeatureSelector,createSelector} from "ngrx/store"
    import {counterFeatureKey,State} from "../reducers/counter.reducer"
    import {AppState} from ".."
    
    export const selectCounter = createFeatureSelector<AppState,State>(counterFeatureKey)
    export const selectCount = createSelector(selectCounter,state=>state.count)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    五十八、Action Payload

    1.在组件中使用dispatch触发Action时传递参数 参数最终会被放置在Action对象中
    this.store.dispatch(increment({count : 5}))
    2.在创建Action Creator函数时 获取参数并指定参数类型
    import {createAction , props } from "ngrx/store"
    export const increment = createAction ("increment",props<{count: number}>())
    
    export declare function props <P extends object>():Props<p>;
    
    3.在Reducer中通过Action对象获取参数
    export const reducer = createReducer(
    initialState,
    on(increment,(state,action)=>({
    count : state.count + action.count
    }))
    )
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    五十九、MetaReducer(中间件)

    metaReducer是Action->Reducer 之间的钩子 允许开发者对Action进行预处理(在普通Reducer函数调用之前调用)
    
    function debug(reducer:ActionReducer<any>):ActionReducer<any>{
    return function (state,action){
    return reducer(state,action)
    }
    }
    
    export const metaReducers: MetaReducer<AppState>[] = !environment.production
    ? [debug]
    : [ ]
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    六十、Effect(处理副作用)

    需求:在页面中新增一个按钮 点击按钮后延迟一秒让数值增加

    1.在组件模板中新增一个用于异步数值增加的按钮,在按钮点击后执行increment_async方法
    <button (click)="increment_as()">async</button>
    2.在组件类中新增increment_as方法 并在方法中触发执行异步操作的Action
    increment_async(){
    this.store.dispatch(increment_async())
    }
    3.在Action文件中新增执行异步操作的Action
    export const increment_async = createAction("increment_async")
    4.创建Effect 接收Action并执  行副作用 继续触发Action
    ng g effect store/effects/counter --
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    六十一、Entity

    翻译是实体的意思 实体就是集合中的一条数据
    NgRx中提供了实体适配器对象 在实体适配器对象下面提供了各种操作集合中实体的方法 目的就是提高开发者操作实体的效率

    核心:
    1.EntityState:实体类型接口——为了约束我们的使用
    {
    ids: [1,2],
    entities:{
    1: {id:1,title:"Hello Angular"}
    2: {id:2,title:"Hello NgRx"}
    }
    }
    export interface State extends EntityState<Todo>{}
    2. createEntityAdapter:创建实体适配器对象
    3.EntityAdapter:实体适配器对象类型接口
    export const adapter : EntityAdapter<Todo> = createEntityAdapter<Todo>()
    //获取初始状态 可以传递对象参数 也可以不传递
    // {ids: [],entities:{}}
    export const initialState :State = adapter.getInitialState()
    
    选择器:
    //selectTotal 获取数据条数
    //selectAll  获取所有数据  以数组形式呈现
    //selectEntities 获取实体集合 以字典形式呈现
    //selectIds 获取id集合 以数组形式呈现
    
    const {selectIds,selectEntities,selectAll,selectTotal}=adapter.getSelectors()
    
    export const selectTodo = createFeatureSelector<AppState,State>(todoFeatureKey)
    export const selectTodos = createSelector(selectTodo,selectAll)
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    六十二、Router Store

    同步路由状态
    1.引入模块
    import {StoreRouterConnectingModule} from "ngrx/router-store"
    
    @NgModule({
    	imports:[
    	   StoreRouterConnectingModule.forRoot()
    ]
    })
    export class AppModule{}
    2.将路由状态集成到Store
    import * as fromRouter from "@ngrx/router-store"
    
    export interface AppState{
       router : fromRouter.RouterReducerState
    }
    
    export const reducers: ActionReducerMap<AppState>={
    	router : fromRouter.routerReducer
    }
    3.创建获取路由状态的Selector
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    六十三、动画

    《1》
    状态:状态表示的是要进行运动的元素在运动的不同时期所呈现的样式
    《2》
    状态的种类:在angular中 有三种类型的状态 分别为:void * custom
    void:当元素在内存中创建好蛋尚未被添加到DOM中或将元素从DOM中删除时会发生此状态

    • : 元素被插入到DOM树之后的状态,或者是已经在DOM树中的元素的状态 也叫默认状态
      custom : 自定义状态 元素默认就在页面之中,从一个状态运动到另一个状态 比如面板的折叠和展开
      《3》进出场动画
      进场动画:是指元素被创建后以动画的形式出现在用户面前 进场动画的状态用void => * 表示 别名为:enter
      出场动画:是指元素在被删除前执行的一段告别动画 出场动画的状态用*=>void 别名为:leave

    六十四、入手

    1》在使用动画之前 需要引入动画模块 即BrowserAnimationModule
    import {BrowserAnimationModule} from "@angular/platform-browser/animations"
    
    @NgModule({
    	imports : [BrowserAnimationModule],
    })
    
    export class AppModule
    《2》默认代码解析 todo之前删除任务和添加任务
    《3》创建动画
    1. trigger方法用于创建动画 指定动画名称
    2. transition 方法用于指定动画的状态 出场动画或者入场动画 或者自定义状态动画
    3. style 方法用于设置元素在不同的状态下所对应的样式
    4. animate方法用于设置运动参数 比如动画运动时间 延迟事件 运动形式
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    六十五、定义动画时的两个注意事项

    《1》入场动画中可以不指定元素的默认状态 Angular会将void状态清空作为默认状态
    《2》要设置动画的运动参数 需要将animate方法的一个参数更改为字符串类型


    六十六、定义帧动画

    关键帧动画使用keyframes方法定义


    六十七、动画回调

    angular提供了喝动画相关的两个回调函数 分别为动画开始执行时喝动画执行完成后
    <li @slide (slide.start) = "start($event)" (@slide.done) = "done($event)"></li>
    
    import {AnimationEvent} from "angular/animations"
    
    start(event:AnimationEvent){
    console.log(event)
    }
    done(event:AnimationEvent){
    console.log(event)
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    六十八、创建可重用动画

    将动画的定义放置在单独的文件中 方便多组件调用


  • 相关阅读:
    浅析能耗分析系统在数据中心节能降耗中的应用
    Cortex-M架构MCU位带操作最详细解析(主要以STM32为例,包括判断哪些MCU可用)
    Adobe Creative Cloud没有管理应用程序的权限怎么办?
    三、性能分析工具的使用
    账户登录问题排查记录
    阿里云国际版CDN的优势
    鸿蒙OS开发:【一次开发,多端部署】(app市场首页)项目
    想看Vue文档,cn放错位置,误入xx网站...
    AS3933国产替代SI3933 125kPKE车钥匙方案
    C#Web开发之blazor体验
  • 原文地址:https://blog.csdn.net/Lixu_No_1/article/details/126189475