angular7学习笔记

安装angular的命令行工具

npm i -g @angular/cli

安装完后执行以下命令创建新项目:

ng new 项目名

然后进入项目目录

cd 项目名

启动开发环境

ng server

快速创建组件(并自动在app.module.ts中导入)

ng g c 组件名

属性绑定

[属性名]="表达式"

如果不加[] 那""里面的就会被当前字符串

如[bgColor]="'red'"等同于 bgColor="red"

如果[bgColor]="red" 这样写, 那编译器会找不到red这个变量

class绑定

[ngClass]="{xxx: 表达式, ... }" 表达式为真则元素就有xxx这个class

style绑定

注意css属性名是驼峰式

[ngStyle]="{backgroundColor: isRed ? 'red' : 'green'}"

条件渲染

*ngIf="表达式"

*ngIf="表达式; else 元素引用名称"

<xxx #名称 >

<div [ngSwitch]="...">
    <div *ngSwitchCase="...">
    </div>
</div>

循环渲染

*ngFor="let item of list"

注意:同一个元素只能有一个*指令

服务类组件

用于封装可复用的业务逻辑代码

一般命名为 xxx.service.ts

创建服务:

import { Injectable } from '@angular/core';

@Injectable()
export class HelloService {
    printHello(): void {
        console.log('hello');
    }
}

在app.module.ts中注册一个provider:

//引入HelloService 
import {HelloService} from './hello.service';
//在构造函数中进行依赖注入
@NgModule({
    ...
    providers: [HelloService],
    ...
})

在需要用到的页面引入该服务:

import {HelloService} from './hello.service';

//在构造函数中进行依赖注入
constructor(private helloService: HelloService) {...}

//调用服务组件的方法
this.helloService.printHello();
  • 服务都是单例的,一旦创建就会被缓存起来 重复使用
  • 服务一旦实例化,将在整个应用的生命周期中存在,可以用来共享数据(下面axios部分有实例)
  • 在需要使用服务的地方,利用依赖注入机制注入服务
  • 依赖注入时自定义的服务需要写在内置的服务后面

父组件 向 子组件 传数据

<sub-component [propName]="value" >

绑定事件监听

<div (click)="myFunction($event)" >

在组件ts文件内定义myFunction方法,其可接收到$event事件对象

自定义事件

<sub-component (myEvent)="doSomething()">

在子组件中,引入EventEmitter,

import {EventEmitter} from "@angular/core";

子组件先声明事件对象

@Output() myEvent = new EventEmitter<boolean>();

子组件的某个方法里调用

this.myEvent.emit(agreed);


解决跨级组件通信

1 创建服务,new一个EventEmitter

import {Injectable, EventEmitter, OnInit} from "@angular/core";
@Injectable()
export class EmitService implements OnInit {
    public eventEmit: any;

    constructor() {
        // 定义发射事件
        this.eventEmit = new EventEmitter();
    }

    ngOnInit() {

    }
}

2、配置module.ts

import {BrowserModule} from '@angular/platform-browser';
import {NgModule} from '@angular/core';

import {AppComponent} from './app.component';
import {EmitComponent} from "./emit.component";
import {EmitService} from "./emit.service";

@NgModule({
    declarations: [
        AppComponent,
        EmitComponent
    ],
    imports: [
        BrowserModule
    ],
    providers: [
        EmitService
    ],
    bootstrap: [
        AppComponent
    ]
})
export class AppModule {
}

3、定义组件,发射消息

import {Component} from '@angular/core';
import {EmitService} from "./emit.service"
@Component({
    selector: 'app-root',
    templateUrl: './app.component.html',
    styleUrls: ['./app.component.css']
})
export class AppComponent {
    constructor(public emitService: EmitService) {

    }

    emitFun() {
        // 如果组件中,修改了某些数据,需要刷新用用户列表,用户列表在其他组件中,那么就可以发射一个字符串过去,那边接收到这个字符串比对一下,刷新列表。
        this.emitService.eventEmit.emit("userList");
    }
}

4、定义接收组件,接收比对发射的字符串,判断,调取接口,刷新组件内容

import {Component, OnInit} from "@angular/core";
import {EmitService} from "./emit.service"
@Component({
    selector: "event-emit",
    templateUrl: "./emit.component.html"
})
export class EmitComonent implements OnInit {
    constructor(public emitService: EmitService) {

    }

    ngOnInit() {
        // 接收发射过来的数据
        this.emitService.eventEmit.subscribe((value: any) => {
           if(value == "userList") {
               // 这里就可以调取接口,刷新userList列表数据
               alert("收到了,我立马刷新列表");
           }
        });
    }

}

总结:其实就是EventEmitter的两个方法,emit(),subscribe()发射和接收;

对象本地指向

(如果是input元素,其类型是HTMLInputElemet)

<div #oneElement > 
<div (click)="func(oneElement)" >
import {ElementRef, ViewChild, ContentChild} from 'angular/core';

//通过 @ViewChild 引用 当前组件模板中的#名称的dom元素 
@ViewChild('名称') 变量名: ElementRef

//通过 @ContentChild 引用 父组件中子组件的子元素#名称的dom元素
@ContentChild('名称') 变量名: ElementRef

内容分发(类似Vue的slot)

子组件利用<ng-content></ng-content> 引用 父组件中此组件标签的子元素.

生命周期

init之前会调constructor()

ngOnChanges(change: SimpleChanges) ;需要import {SimpleChanges } from '@angular/core';

ngOnInit

ngDoCheck

ngAfterContentInit

ngAfterContentChecked

ngAfterViewInit

ngAfterViewChecked

ngOnDestroy

class xxx implements OnInit, OnChanges

自定义指令

@Directive({
        selector: '[指令名]'
})
export class 类名 implements OnInit {
    constructor(private elementRef: ElementRef, renderer: Renderer2) {
         
    }
    ngOnInit() {
this.renderer.setStyle(this.elementRef.nativeElement, 'background-color', 'red');
} }

构造函数的参数elementRef就是调用这个指令的元素,前面应该加上private,否则在类中其它方法中引用this.el时会报错

调用时就是在元素中用上面的指令名做为属性名, 不需要加[]或*,

使用之前还要在ngModule的declarations中声明(官方文档里居然没有写这部分)

@ngModule({
    declarations: {
        指令类名
    }
})

我用ionic g d 指令名 这个命令生成的指令, 它自动生成了"directives"文件夹,其下生成了 directives.module.ts 和 "指令名/指令名.directive.ts", directives.module.ts 内容如下:

import { NgModule } from '@angular/core';
import { HightLightDirective } from './hight-light/hight-light';
@NgModule({
    declarations: [HightLightDirective],
    imports: [],
    exports: [HightLightDirective]
})
export class DirectivesModule {}

同时,如果使用 directives.module.ts , 那么,在app.module.ts中也可以这样引用并声明指令:

import { DirectivesModule } from '../directives/directives.module';

@NgModule({
    ...
    //导入 模块
    imports: [
        DirectivesModule,
        ...
    ],
    ...
})

用命令行工具创建指令

ng g d 路径/my-name

指令文件名小写以-连接,生成文件名为my-name.directive.ts,

生成的指令类名为MyNameDirectvie, 选择器名为appMyName,

在指令类中,通过@hostlistener监听事件

import {HostListener} from '@angular/core';
@HostListener('mouseenter') dosomething(event: Event) {}

在指令类中,通过@HostBinding绑定属性,将指令内的变量绑定到引用元素上的属性值

import {HostBinding} from '@angular/core';
@HostBinding('style.backgroundColor') bgc:strind = 'red';

还可以通过引用元素的属性向指令传递参数,方法与经组件传参数一样,用@input()

路由

1.在app-routing.module.ts中引用路由模块

(*以下代码,在使用ng new 创建项目时如果选择了使用路由,那么将会自动创建)

import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';

const routes: Routes = [];

@NgModule({
  imports: [RouterModule.forRoot(routes)],
  exports: [RouterModule]
})
export class AppRoutingModule { }

2.引入组件

在根模块中引入和声明组件, (如果是通过命令创建的组件,则工具会自动完成这步)

在app-routing.module.ts中引入组件

import { MyComponent } from "path/to/MyComponent";

3.定义路由规则

const routes: Routes = [
    {path: 'path1', component: MyComponent1},
    {path: 'path2', component: MyComponent2},
    ....
];

path如果以/开头就是绝对路径,指向网站根路径,即http://domain/, 否则就是相对路径,相对于当前 定义路由的模块的路径.

比如在 app-routing.module.ts 中定义了一条path: news, 访问时就是 http://domain/news

*在浏览器中访问时路径区分大小写

在根组件模板中添加"路由出口",即在HTML中路由动态加载组件的地方

<router-outlet></router-outlet>

默认路由

  {path: '**', component: HomeComponent}

  {path: '**', redirectTo: 'home'}

'**'表示匹配任意路径,(实测发现 '' 也可以)

注意,这个默认路由规则 必须 写在后面,如果写在前面,那其它规则就都失效了

创建跳转链接, 在一个标签中使用

[routerLink]="[ '/news' ]"

路径如果是相对路径,则是相对当前的url,

当前激活路由,给他一个类用来指定激活路由的样式

routerLinkActive="active"

路由传参

get方式

//在html中跳转连接上加上
[queryParams]="{id:123}"

//跳转的目标组件引入
import { ActivatedRoute } from '@angular/router';

//目标组件声明
constructor(public route: ActivatedRoute) {}
//接收参数
this.route.queryParams.subscribe((data)=>{   
console.log(data); // {id: 123}
})

动态路由

//定义路由规则 , 只要在path的值中加入:参数名就可以了
{path: 'home/:id', component: HomeComponent}

//定义连接
[routerLink]="['/path/',  id]"

//目标组件中仍然要引入ActivatedRoute
import {ActivatedRoute} from '@angular/router';

//在构造函数中声明
constructor(public route: ActivatedRoute) {}

//接收参数,与get传参唯一不同的是.params
this.route.params.subscribe((data) => {
    console.log(data); //{id: xxx}
});
//官方文档中是这样写的: (paramMap和params有什么区别?)
this.route.paramMap.subscribe(params => {
this.product = products[+params.get('productId')];
});

编程方式路由跳转

在JS代码中用路由的API进行跳转, 上面的跳转都是通过html中添加[routerLink]属性

编程路由跳转也分为GET传值和 动态路由 两种方式

编程式动态路由跳转:

//同样需要在路由规则中配置动态路由规则
{path: 'home/:id', component: HomeComponent}

//在要调用路由跳转API的组件中引入路由模块
import {Router} from '@angular/router';

//在构造函数上声明
contructor(public router: Router) {}

//调用, navigate的第一个参数是一个数组
this.router.navigate(['/path', param]);

//接收
this.route.params.subscribe((data) => {
    console.log('在news页中接收到',data); // {id: 123}
})

编程式GET传参路由跳转

//引入
import { Router, NavigationExtras } from '@angular/router';


//定义路由参数
let navigationExtras: NavigationExtras = {
    queryParams: {id: 123},
    fragment: 'anchor'
}
this.router.navigate(['/path'], navigationExtras);

//接收
this.route.queryParams.subscribe((data) => {
    console.log('在news页中接收到',data); // {id: 123}
});

父子路由/嵌套路由

//在路由规则中定义子中由
const routes: Routes = [
  {
        path: '...',component: ...,
        children: [
    {path: ..., component: ...},
    ...
        ]
    },
]
//跳转
<a [routerLink]="['/path/to']"  ></a>
//或
<a [routerLink]="['to']"  ></a>
//在父组件中加入 组件模板出口
<router-outlet></router-outlet>

HTTP数据请求

GET请求

1.在app.module.ts引入HttpClientModule .

import {HttpClientModule} from '@aunglar/common/http';
imports: [..., HttpClientModule, ...]

2.在用到的地方引入

import {HttpClient} from '@angular/common/http';
并在构造函数声明
constructor(public http: HttpClient) {}

3.使用get请求数据

this.http.get(url).subscribe((response)=>{});

POST请求

1.在app.module.ts引入HttpClientModule .与上面一样(略)

2.在用到的地方引入

import {HttpClient, HttpHeaders} from '@angular/common/http';

3.定义http请求选项

const httpOptions = {
    headers: new HttpHeaders({'Content-Type', 'application/json'});
};    

4.调用post请求

this.http.post(url, {...}, httpOptions).subscribe((response)=>{});

JSONP请求

1.在app.module.ts引入HttpClientModule 和 HttpClientJsonpModule

import { HttpClientModule, HttpClientJsonpModule } from '@aunglar/common/http';
imports: [..., HttpClientModule, HttpClientJsonpModule, ...]

2.在用到的地方引用HttpClientModule,(同get请求,略)

并在构造函数声明

3.使用get请求数据

this.http.jsonp(url, 'callback').subscribe((response)=>{});

注: 第二个参数根据实际服务器的情况设置,可以通过在浏览器地址栏直接访问接口地址,观察返回结果,如果开头是callback就写callback,是什么第二个参数就写什么

另:http也提供了put,delete 方法

使用axios请求数据

安装

npm i axios -S

可以新建一个服务, 如果不用服务封装,也可以直接在使用的组件中直接引入axios并使用

ng g service service/http

在其中引入

import axios from 'axios';

调用

axiosGet(url) {
return new Promise((resolve, reject)=>{
axios.get(url).then((response)=>{      resolve(response);    });
});
}

在app.module.ts中引入服务

import httpService from './service/httpservice.service';

并在providers中声明

providers: [..., httpService, ...]

在使用服务的文件中也要引入服务 (略)

在构造函数的参数中声明服务对象

constructor(...,public httpService: HttpService) {}

调用

this.httpService.axiosGet(url).then(...)