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(...)