Angular7入门总结篇
62019.01.08 19:34:05 字数 4854阅读 46093
发表于 http://blog.poetries.top/2019/01/09/angular7-intro-summary
最近在学Ionic4
,不得不学习angular
、Typescript
基础知识,这里简单总结记录一下。
一、Angular 介绍
Angualr
是一款来自谷歌的开源的 web
前端框架,诞生于 2009
年,由 Misko Hevery
等 人创建,后为 Google
所收购。是一款优秀的前端 JS
框架,已经被用于 Google
的多款产品当中。
根据项目数统计 angular(1.x 、2.x 、4.x、5.x、6.x、7.x)
是现在网上使用量最大的框架
Angualr
基于 TypeScript
和 react
、vue
相比, Angular
更适合中大型企业级项目。
目前 2018 年 11 月 25 日 angular
最新版本 angular7.x
。根据官方介绍,Angular
每过几个月 就会更新一个版本。此教程同样适用于后期更新的 Angular8.x
、Angular9.x
学习 Angular 必备基础
必备基础:html
、css
、js
、es6
、Typescript
二、Angular 环境搭建及创建项目
2.1 环境搭建
1. 安装 nodejs
安装 angular
的计算机上面必须安装最新的 nodejs
--注意安装 nodejs
稳定版本
2. 安装 cnpm
npm 可能安装失败建议先用 npm
安装一下 cnpm
用淘宝镜像安装https://npm.taobao.org
npm install -g cnpm --registry=https://registry.npm.taobao.org
3. 使用 npm/cnpm 命令安装 angular/cli
npm install -g @angular/cli
# 或者
cnpm install -g @angular/cli
ng v
查看版本信息
4. 安装插件
5. 安装chrome扩展
用augury
查看component
结构,更方便调试
2.2 创建项目
# 创建项目
ng new my-app
cd my-app
# 运行项目
ng serve --open
2.3 目录结构分析
详情参考 https://www.angular.cn/guide/file-structure
app目录(重点)
app
目录是我们要编写的代码目录。我们写的代码都是放在这个目录。 一个Angular
程序至少需要一个模块和一个组件。在我们新建项目的时候命令行已经默认生成出来了
app.component.ts
:这个文件表示组件,
组件是Angular
应用的基本构建模块,可以理解为一段带有业务逻辑和数据的Html
我们来看看app.component.ts
中的代码,并解释下代码的意义
/*这里是从Angular核心模块里面引入了component装饰器*/
import {Component} from '@angular/core';
/*用装饰器定义了一个组件以及组件的元数据 所有的组件都必须使用这个装饰器来注解*/
@Component({
/*组件元数据 Angular会通过这里面的属性来渲染组件并执行逻辑
* selector就是css选择器,表示这个组件可以通过app-root来调用,index.html中有个Loading... 标签,这个标签用来展示该组件的内容
*templateUrl 组件的模板,定义了组件的布局和内容
*styleUrls 该模板引用那个css样式
* */
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
/*AppComponent本来就是一个普通的typescript类,但是上面的组件元数据装饰器告诉Angular,AppComponent是一个组件,需要把一些元数据附加到这个类上,Angular就会把AppComponent当组件来处理*/
export class AppComponent {
/*这个类实际上就是该组件的控制器,我们的业务逻辑就是在这个类中编写*/
title = '学习Angular';
}
组件相关的概念
组件元数据装饰器(@Component
) 简称组件装饰器,用来告知Angular
框架如何处理一个TypeScript
类. Component
装饰器包含多个属性,这些属性的值叫做元数据,Angular
会根据这些元数据的值来渲染组件并执行组件的逻辑
模板(Template
) 我们可以通过组件自带的模板来定义组件的外观,模板以html
的形式存在,告诉Angular
如何来渲染组件,一般来说,模板看起来很像html
,但是我们可以在模板中使用Angular
的数据绑定语法,来呈现控制器中的数据。
控制器(controller
) 控制器就是一个普通的typescript
类,他会被@Component
来装饰,控制器会包含组件所有的属性和方法,绝大多数的业务逻辑都是写在控制器里的。控制器通过数据绑定与模板来通讯,模板展现控制器的数据,控制器处理模板上发生的事件。
装饰器 ,模板和控制器是组件的必备要素。还有一些可选的元素,比如:
输入属性(@inputs
):是用来接收外部传入的数据的,Angular
的程序结构就是一个组件树,输入属性允许在组件树种传递数据 提供器(providers
):这个是用来做依赖注入的
生命周期钩子(LifeCycle Hooks
):一个组件从创建到销毁的过程中会有多个钩子会被触发,类似于Android中的Activity
的生命周期
样式表:组件可以关联一些样式表
动画(Animations
): Angular
提供了一个动画包来帮助我们方便的创建一些跟组件相关的动画效果,比如淡入淡出等
输出属性(@Outputs
):用来定义一些其他组件可能需要的事件或者用来在组件之间共享数据
组件的中关系就如下图所示
下面我们来看看模块文件
app.module.ts
:这个文件表示模块
与AppComponent
类似,模块也需要装饰器来装饰
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { HeroesComponent } from './heroes/heroes.component';
@NgModule({
declarations: [
// 声明模块里有什么东西 只能声明:组件/指令/管道
AppComponent,
HeroesComponent
],
// 声明该模块所依赖的模块
imports: [
BrowserModule,
AppRoutingModule
],
// 默认情况下是空的
providers: [],
// 声明模块的主组件是什么
bootstrap: [AppComponent]
})
export class AppModule { }
2.4 Angular cli
https://cli.angular.io
通过ng g
列出当前命令
1. 创建新组件 ng generate component component-name
ng g component components/header
指定生成到哪个目录
该命令会把生成的组件,添加到 src/app/app.module.ts
文件中 @NgModule
的 declarations
列表中声明
2. 使用 Angular CLI 创建一个名叫 hero 的服务
ng generate service hero
该命令会在 src/app/hero.service.ts
中生成 HeroService
类的骨架。 HeroService
类的代码如下:
import { Injectable } from '@angular/core';
@Injectable({
providedIn: 'root',
})
export class HeroService {
constructor() { }
}
3. 添加 AppRoutingModule
ng generate module app-routing --flat --module=app
--flat
把这个文件放进了 src/app
中,而不是单独的目录中。
--module=app
告诉 CLI
把它注册到 AppModule
的 imports
数组中。
生成的文件是这样的:
src/app/app-routing.module.ts (generated)
content_copy
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
@NgModule({
imports: [
CommonModule
],
declarations: []
})
export class AppRoutingModule { }
修改后
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
const routes: Routes = [];
@NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule]
})
export class AppRoutingModule { }
三、angular组件及组件里的模板
3.1 创建angualr组件
1. 创建组件
ng g component components/header
2. 使用组件
3.2 Angular 绑定数据
1. 数据文本绑定
定义数据几种方式
{{title}}
2. 绑定HTML
this.h="这是一个 h2 用[innerHTML]来解析 "
3.3 声明属性的几种方式
public
共有(默认) 可以在类里外使用
protected
保护类型 只能在当前类和子类中使用
private
私有类型 只能在当期类使用
3.4 绑定属性
用[]
包裹
调试工具看看我的属性
3.5 数据循环 *ngFor
*1. ngFor 普通循环
export class HomeComponent implements OnInit {
arr = [{ name: 'poetries', age: 22 }, { name: 'jing' , age: 31}];
constructor() { }
ngOnInit() {
}
}
0">
{{item.name}}- {{item.age}}
2. 循环的时候设置 key
3. template 循环数据
3.6 条件判断 *ngIf
3">这是 ngIF 判断是否显示
这是 ngIF 判断是否显示
3.7 *ngSwitch
3.8 执行事件 (click)=”getData()”
点击按钮触发事件
点击按钮设置数据
getData(){ /*自定义方法获取数据*/ //获取
alert(this.msg);
}
setData(){
//设置值
this.msg='这是设置的值';
}
3.9 表单事件
keyUpFn(e){
console.log(e)
}
3.10 双向数据绑定
注意引入:FormsModule
import {FormsModule} from '@angular/forms'
NgModule({
declarations: [
AppComponent,
HeaderComponent,
FooterComponent,
NewsComponent
],
imports: [
BrowserModule,
FormsModule
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
{{inputValue}}
3. 11 [ngClass]、[ngStyle]
1. [ngClass]:
这是一个 div
public flag=false;
这是一个 div
public arr = [1, 3, 4, 5, 6];
2. [ngStyle]:
你好 ngStyle
public attr='red';
你好 ngStyle
3.12 管道
public today=new Date();
{{today | date:'yyyy-MM-dd HH:mm:ss' }}
其他管道
angular
中的管道(pipe
)是用来对输入的数据进行处理,如大小写转换、数值和日期格式化等
angular
中的管道(pipe
) 以及自定义管道适用于angular4 angualr5 angualr6 angular7
常用的管道(pipe
)有
1. 大小写转换
{{str | uppercase}}
{{str | lowercase}}
2. 日期格式转换
{{today | date:'yyyy-MM-dd HH:mm:ss' }}
3. 小数位数
接收的参数格式为{最少整数位数}.{最少小数位数}-{最多小数位数}
{{p | number:'1.2-4'}}
4. JavaScript 对象序列化
{{ { name: 'semlinker' } | json }}
5. slice
{{ 'semlinker' | slice:0:3 }}
6. 管道链
{{ 'semlinker' | slice:0:3 | uppercase }}
7. 自定义管道
自定义管道的步骤:
使用 @Pipe
装饰器定义 Pipe
的 metadata
信息,如 Pipe
的名称 - 即 name
属性
实现 PipeTransform
接口中定义的 transform
方法
7.1 WelcomePipe 定义
import { Pipe, PipeTransform } from '@angular/core';
[@Pipe](/user/Pipe)({ name: 'welcome' })
export class WelcomePipe implements PipeTransform {
transform(value: string): string {
if(!value) return value;
if(typeof value !== 'string') {
throw new Error('Invalid pipe argument for WelcomePipe');
}
return "Welcome to " + value;
}
}
7.2 WelcomePipe 使用
{{ 'semlinker' | welcome }}
{{ 'semlinker' | welcome }}
7.3 RepeatPipe 定义
import {Pipe, PipeTransform} from '@angular/core';
[@Pipe](/user/Pipe)({name: 'repeat'})
export class RepeatPipe implements PipeTransform {
transform(value: any, times: number) {
return value.repeat(times);
}
}
7.4 RepeatPipe 使用
{{ 'lo' | repeat:3 }}
{{ 'lo' | repeat:3 }}
3.13 实现一个人员登记表单-案例
# 创建组件
ng g component components/form
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'app-form',
templateUrl: './form.component.html',
styleUrls: ['./form.component.scss']
})
export class FormComponent implements OnInit {
public peopleInfo:any = {
username: '',
sex: '2',
cityList: ['北京', '上海', '深圳'],
city: '上海',
hobby:[{
title: '吃饭',
checked:false
},{
title:'睡觉',
checked:false
},{
title:'敲代码',
checked:true
}],
mark:''
}
constructor() { }
ngOnInit() {
}
doSubmit(){
/*
jquery dom操作
let usernameDom:any=document.getElementById('username');
console.log(usernameDom.value);
*/
console.log(this.peopleInfo);
}
}
人员登记系统
获取表单的内容
{{peopleInfo | json}}
h2{
text-align: center;
}
.people_list{
width: 400px;
margin: 40px auto;
padding:20px;
border:1px solid #eee;
li{
height: 50px;
line-height: 50px;
.fonm_input{
width: 300px;
height: 28px;
}
}
.submit{
width: 100px;
height: 30px;
float: right;
margin-right: 50px;
margin-top:120px;
}
}
3.14 实现一个完整的ToDo-案例
基础版
# 创建组件
ng g component components/todo
todoList
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'app-todo',
templateUrl: './todo.component.html',
styleUrls: ['./todo.component.scss']
})
export class TodoComponent implements OnInit {
public keyword: string;
public todolist: any[] = [];
constructor() { }
ngOnInit() {
}
doAdd(e){
if(e.keyCode == 13){
if(!this.todolistHasKeyword(this.todolist, this.keyword)){
this.todolist.push({
title: this.keyword,
status: 0 //0表示代办事项 1表示已完成事项
});
this.keyword='';
}else{
alert('数据已经存在');
this.keyword='';
}
}
}
deleteData(key){
this.todolist.splice(key,1);
}
//如果数组里面有keyword返回true 否则返回false
todolistHasKeyword(todolist:any, keyword:any){
//异步 会存在问题
// todolist.forEach(value => {
// if(value.title==keyword){
// return true;
// }
// });
if(!keyword) return false;
for(var i=0; i
h2{
text-align: center;
}
.todolist{
width: 400px;
margin: 20px auto;
.form_input{
margin-bottom: 20px;
width: 300px;
height: 32px;
}
li{
line-height: 60px;
}
}
3.15 搜索缓存数据-案例
基础版
# 创建组件
ng g component components/search
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'app-search',
templateUrl: './search.component.html',
styleUrls: ['./search.component.scss']
})
export class SearchComponent implements OnInit {
public keyword: string;
public historyList: any[] = [];
constructor() { }
ngOnInit() {
}
doSearch(){
if(this.historyList.indexOf(this.keyword)==-1){
this.historyList.push(this.keyword);
}
this.keyword = '';
}
deleteHistroy(key){
alert(key);
this.historyList.splice(key,1);
}
}
.search{
width: 400px;
margin: 20px auto;
input{
margin-bottom: 20px;
width: 300px;
height: 32px;
}
button{
height: 32px;
width: 80px;
}
}
四、Angular 中的服务
4.1 服务
定义公共的方法,使得方法在组件之间共享调用
1. 创建服务命令
ng g service my-new-service
# 创建到指定目录下面
ng g service services/storage
2. app.module.ts 里面引入创建的服务
// app.module.ts 里面引入创建的服务
import { StorageService } from './services/storage.service';
// NgModule 里面的 providers 里面依赖注入服务
NgModule({
declarations: [
AppComponent,
HeaderComponent,
FooterComponent,
NewsComponent,
TodolistComponent
], imports: [
BrowserModule,
FormsModule
],
providers: [StorageService],
bootstrap: [AppComponent]
})
export class AppModule { }
3. 使用的页面引入服务,注册服务
import { StorageService } from '../../services/storage.service';
constructor(private storage: StorageService) {
}
// 使用
addData(){
// alert(this.username);
this.list.push(this.username);
this.storage.set('todolist',this.list);
}
removerData(key){
console.log(key);
this.list.splice(key,1);
this.storage.set('todolist',this.list);
}
4.2 改造上面的Todo、searchList
searchList
import { Component, OnInit } from '@angular/core';
// 引入服务
import { StorageService } from '../../services/storage.service';
@Component({
selector: 'app-search',
templateUrl: './search.component.html',
styleUrls: ['./search.component.scss']
})
export class SearchComponent implements OnInit {
public keyword: string;
public historyList: any[] = [];
constructor(public storage: StorageService) {
console.log(this.storage.get());
}
ngOnInit() {
// 修改的地方
var searchlist:any=this.storage.get('searchlist');
if(searchlist){
this.historyList=searchlist;
}
}
doSearch(){
if(this.historyList.indexOf(this.keyword)==-1){
this.historyList.push(this.keyword);
// 修改的地方
this.storage.set('searchlist',this.historyList);
}
this.keyword = '';
}
deleteHistroy(key){
alert(key);
this.historyList.splice(key,1);
}
}
TODOLIST
ngOnInit() {
// 修改的地方
var todolist:any=this.storage.get('todolist');
if(todolist){
this.todolist=todolist;
}
}
doAdd(e){
if(e.keyCode==13){
if(!this.todolistHasKeyword(this.todolist,this.keyword)){
this.todolist.push({
title:this.keyword,
status:0 //0表示代办事项 1表示已完成事项
});
this.keyword='';
// 修改的地方
this.storage.set('todolist',this.todolist); //用到this一定要注意this指向
}else{
alert('数据已经存在');
this.keyword='';
}
}
}
// 修改的地方
checkboxChange(){
console.log('事件触发了');
this.storage.set('todolist',this.todolist);
}
todoList
五、Dom 操作以及@ViewChild、 执行 css3 动画
1. Angular 中的 dom 操作(原生 js)
ngAfterViewInit(){
var boxDom:any=document.getElementById('box'); boxDom.style.color='red';
}
2. Angular 中的 dom 操作(ViewChild)
import { Component ,ViewChild,ElementRef} from '@angular/core';
@ViewChild('myattr') myattr: ElementRef;
ngAfterViewInit(){
let attrEl = this.myattr.nativeElement;
}
3. 父子组件中通过 ViewChild 调用子组件 的方法
调用子组件给子组件定义一个名称
引入 ViewChild
import { Component, OnInit ,ViewChild} from '@angular/core';
ViewChild
和刚才的子组件关联起来
@ViewChild('footerChild') footer
在父组件中调用子组件方法
run(){
this.footer.footerRun();
}
六、Angular 父子组件以及组件之间通讯
6.1 父组件给子组件传值-@input
父组件不仅可以给子组件传递简单的数据,还可把自己的方法以及整个父组件传给子组件
1. 父组件调用子组件的时候传入数据
2. 子组件引入 Input 模块
import { Component, OnInit ,Input } from '@angular/core';
3. 子组件中 @Input 接收父组件传过来的数据
export class HeaderComponent implements OnInit {
@Input() msg:string
constructor() { }
ngOnInit() {
}
}
4. 子组件中使用父组件的数据
child works!
{{msg}}
5. 把整个父组件传给子组件
通过this
传递整个组件实例
export class HeaderComponent implements OnInit {
@Input() home:any
constructor() { }
ngOnInit() {
}
}
执行父组件方法 this.home.xxx()
6.2 子组件通过@Output 触发父组件的方法(了解)
1. 子组件引入 Output 和 EventEmitter
import { Component, OnInit ,Input,Output,EventEmitter} from '@angular/core';
2. 子组件中实例化 EventEmitter
@Output() private outer=new EventEmitter(); /*用EventEmitter和output装饰器配合使用 指定类型变量*/
3. 子组件通过 EventEmitter 对象 outer 实例广播数据
sendParent(){
// alert('zhixing');
this.outer.emit('msg from child')
}
4. 父组件调用子组件的时候,定义接收事件 , outer 就是子组件的 EventEmitter 对象 outer
5. 父组件接收到数据会调用自己的 runParent 方法,这个时候就能拿到子组件的数据
//接收子组件传递过来的数据
runParent(msg:string){
alert(msg);
}
6.3 父组件通过@ViewChild 主动获取子组 件的数据和方法
1. 调用子组件给子组件定义一个名称
2. 引入 ViewChild
import { Component, OnInit ,ViewChild} from '@angular/core';
3. ViewChild 和刚才的子组件关联起来
@ViewChild('footerChild') footer;
4. 调用子组件
run(){ this.footer.footerRun();
}
6.4 非父子组件通讯
公共的服务
Localstorage
(推荐)
Cookie
七、Angular 中的生命周期函数
7.1 Angular中的生命周期函数
官方文档:https://www.angular.cn/guide/lifecycle-hooks
生命周期函数通俗的讲就是组件创建、组件更新、组件销毁的时候会触发的一系列的方法。
当 Angular
使用构造函数新建一个组件或指令后,就会按下面的顺序在特定时刻调用这些 生命周期钩子方法。
每个接口都有唯一的一个钩子方法,它们的名字是由接口名再加上ng
前缀构成的,比如OnInit
接口的钩子方法叫做ngOnInit
.
1. 生命周期钩子分类
基于指令与组件的区别来分类
指令与组件共有的钩子
ngOnChanges
ngOnInit
ngDoCheck
ngOnDestroy
组件特有的钩子
ngAfterContentInit
ngAfterContentChecked
ngAfterViewInit
ngAfterViewChecked
2. 生命周期钩子的作用及调用顺序
1、ngOnChanges
- 当数据绑定输入属性的值发生变化时调用 2、ngOnInit
- 在第一次 ngOnChanges
后调用 3、ngDoCheck
- 自定义的方法,用于检测和处理值的改变 4、ngAfterContentInit
- 在组件内容初始化之后调用 5、ngAfterContentChecked
- 组件每次检查内容时调用 6、ngAfterViewInit
- 组件相应的视图初始化之后调用 7、ngAfterViewChecked
- 组件每次检查视图时调用 8、ngOnDestroy
- 指令销毁前调用 3. 首次加载生命周期顺序
export class LifecircleComponent {
constructor() {
console.log('00构造函数执行了---除了使用简单的值对局部变量进行初始化之外,什么都不应该做')
}
ngOnChanges() {
console.log('01ngOnChages执行了---当被绑定的输入属性的值发生变化时调用(父子组件传值的时候会触发)');
}
ngOnInit() {
console.log('02ngOnInit执行了--- 请求数据一般放在这个里面');
}
ngDoCheck() {
console.log('03ngDoCheck执行了---检测,并在发生 Angular 无法或不愿意自己检测的变化时作出反应');
}
ngAfterContentInit() {
console.log('04ngAfterContentInit执行了---当把内容投影进组件之后调用');
}
ngAfterContentChecked() {
console.log('05ngAfterContentChecked执行了---每次完成被投影组件内容的变更检测之后调用');
}
ngAfterViewInit() : void {
console.log('06 ngAfterViewInit执行了----初始化完组件视图及其子视图之后调用(dom操作放在这个里面)');
}
ngAfterViewChecked() {
console.log('07ngAfterViewChecked执行了----每次做完组件视图和子视图的变更检测之后调用');
}
ngOnDestroy() {
console.log('08ngOnDestroy执行了····');
}
//自定义方法
changeMsg() {
this.msg = "数据改变了";
}
}
带check
的可以对数据做响应操作
数据改变了
点击按钮/双向数据绑定此时触发了以下生命周期。只要数据改变
可以在check
做一些操作
ngDoCheck() {
//写一些自定义的操作
console.log('03ngDoCheck执行了---检测,并在发生 Angular 无法或不愿意自己检测的变化时作出反应');
if(this.userinfo!==this.oldUserinfo){
console.log(`你从${this.oldUserinfo}改成${this.userinfo}`);
this.oldUserinfo = this.userinfo;
}else{
console.log("数据没有变化");
}
}
7.2 生命周期钩子详解
7.2.1 constructor-掌握
constructor
,来初始化类。Angular
中的组件就是基于class
类实现的,在Angular
中,constructor
用于注入依赖。组件的构造函数会在所有的生命周期钩子之前被调用,它主要用于依赖注入或执行简单的数据初始化操作。
import { Component, ElementRef } from '@angular/core';
@Component({
selector: 'my-app',
template: `
Welcome to Angular World
Hello {{name}}
`,
})
export class AppComponent {
name: string = '';
constructor(public elementRef: ElementRef) {//使用构造注入的方式注入依赖对象
// 执行初始化操作
this.name = 'Semlinker';
}
}
7.2.2 ngOnChanges()
当 Angular
(重新)设置数据绑定输入属性时响应。该 方法接受当前和上一属性值的 SimpleChanges
对象 当被绑定的输入属性的值发生变化时调用,首次调用一 定会发生在 ngOnInit()
之前。
此时改变title
会触发ngOnChanges
生命周期,并且也会触发
7.2.3 ngOnInit()--掌握
在 Angular
第一次显示数据绑定和设置指令/组件的输入属性之后,初始化指令/组件。在第一轮 ngOnChanges()
完成之后调用,只调用一次。可以请求数据
使用 ngOnInit() 有两个原因 :
在构造函数之后马上执行复杂的初始化逻辑
在 Angular
设置完输入属性之后,对该组件进行准备。有经验的开发者会认同组件的构建应该很便宜和安全
import { Component, Input, OnInit } from '@angular/core';
@Component({
selector: 'exe-child',
template: `
父组件的名称:{{pname}}
`
})
export class ChildComponent implements OnInit {
@Input()
pname: string; // 父组件的名称
constructor() {
console.log('ChildComponent constructor', this.pname);
// Output:undefined
}
ngOnInit() {
console.log('ChildComponent ngOnInit', this.pname);
// output: 输入的pname值
}
}
7.2.4 ngDoCheck()
检测,并在发生 Angular
无法或不愿意自己检测的变 化时作出反应。在每个 Angular
变更检测周期中调用, ngOnChanges()
和 ngOnInit()
之后。
7.2.5 ngAfterContentInit()
当把内容投影进组件之后调用。第一次 ngDoCheck()
之后调用,只调用一次
7.2.6 ngAfterContentChecked()
每次完成被投影组件内容的变更检测之后调用。 ngAfterContentInit()
和每次 ngDoCheck()
之后调
7.2.7 ngAfterViewInit()--掌握
初始化完组件视图及其子视图之后调用。第一 次 ngAfterContentChecked()
之后调用,只调用一次。在这里可以操作DOM
7.2.8 ngAfterViewChecked()
每次做完组件视图和子视图的变更检测之后调用。 ngAfterViewInit()
和每次 ngAfterContentChecked()
之后 调用。
7.2.9 ngOnDestroy()--掌握
当 Angular
每次销毁指令/组件之前调用并清扫。在这儿反订阅可观察对象和分离事件处理器,以防内存泄 漏。在 Angular
销毁指令/组件之前调用。比如:移除事件监听、清除定时器、退订 Observable
等。
@Directive({
selector: '[destroyDirective]'
})
export class OnDestroyDirective implements OnDestroy {
sayHello: number;
constructor() {
this.sayHiya = window.setInterval(() => console.log('hello'), 1000);
}
ngOnDestroy() {
window.clearInterval(this.sayHiya);
}
}
八、Rxjs 异步数据流编程
8.1 Rxjs介绍
参考手册:https://www.npmjs.com/package/rxjs
中文手册:https://cn.rx.js.org/
RxJS
是 ReactiveX
编程理念的 JavaScript
版本。ReactiveX
来自微软,它是一种针对异步数据 流的编程。简单来说,它将一切数据,包括 HTTP
请求,DOM
事件或者普通数据等包装成流的形式,然后用强大丰富的操作符对流进行处理,使你能以同步编程的方式处理异步数据,并组合不同的操作符来轻松优雅的实现你所需要的功能。
RxJS
是一种针对异步数据流编程工具,或者叫响应式扩展编程;可不管如何解释 RxJS 其目 标就是异步编程,Angular
引入 RxJS
为了就是让异步可控、更简单。
RxJS
里面提供了很多模块。这里我们主要给大家讲 RxJS
里面最常用的Observable
和 fromEvent
目前常见的异步编程的几种方法:
回调函数
事件监听/发布订阅
Promise
Rxjs
8.2 Promise和RxJS处理异步对比
新建一个services
ng g service services/rxjs
在services/rxjs.service.ts
中写以下方法
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs';
@Injectable({
providedIn: 'root'
})
export class RxjsService {
constructor() { }
// Promise 处理异步
getPromiseData() {
return new Promise(resolve = >{
setTimeout(() = >{
resolve('---promise timeout---');
},
2000);
});
// RxJS 处理异步:
getRxjsData() {
return new Observable(observer = >{
setTimeout(() = >{
observer.next('observable timeout');
},
2000);
});
}
}
// 在其他组件使用服务
import { Component, OnInit } from '@angular/core';
import { RxjsService } from '../../services/rxjs.service';
@Component({
selector: 'app-rxjs',
templateUrl: './rxjs.component.html',
styleUrls: ['./rxjs.component.scss']
})
export class RxjsComponent implements OnInit {
// 注入服务
constructor(public request: RxjsService) {
}
ngOnInit() {
// 调用方法
this.request.getRxjsData().subscribe(data=>{
console.log(data)
})
}
}
从上面列子可以看到 RxJS
和 Promise
的基本用法非常类似,除了一些关键词不同。Promise
里面用的是 then()
和 resolve()
,而 RxJS
里面用的是 next()
和 subscribe()
Rxjs
相比Promise
要强大很多。 比如 Rxjs
中可以中途撤回、Rxjs
可以发射多个值、Rxjs
提供了多种工具函数等等
8.3 Rxjs unsubscribe 取消订阅
Promise
的创建之后,动作是无法撤回的。Observable
不一样,动作可以通过 unsbscribe()
方法中途撤回,而且 Observable
在内部做了智能的处理.
Promise 创建之后动作无法撤回
let promise = new Promise(resolve = >{
setTimeout(() = >{
resolve('---promise timeout---');
},
2000);
});
promise.then(value = >console.log(value));
Rxjs 可以通过 unsubscribe() 可以撤回 subscribe 的动作
let stream = new Observable(observer = >{
let timeout = setTimeout(() = >{
clearTimeout(timeout);
observer.next('observable timeout');
},
2000);
});
let disposable = stream.subscribe(value = >console.log(value));
setTimeout(() = >{
//取消执行 disposable.unsubscribe();
},
1000);
8.4 Rxjs 订阅后多次执行
如果我们想让异步里面的方法多次执行,比如下面代码。
这一点 Promise
是做不到的,对于 Promise
来说,最终结果要么 resole
(兑现)、要么 reject
(拒绝),而且都只能触发一次。如果在同一个 Promise
对象上多次调用 resolve
方法, 则会抛异常。而 Observable
不一样,它可以不断地触发下一个值,就像 next()
这个方法的 名字所暗示的那样。
let promise = new Promise(resolve = >{
setInterval(() = >{
resolve('---promise setInterval---');
},
2000);
});
promise.then(value = >console.log(value));
Rxjs
let stream = new Observable < number > (observer = >{
let count = 0;
setInterval(() = >{
observer.next(count++);
},
1000);
});
stream.subscribe(value = >console.log("Observable>" + value));
8.5 Angualr6.x之前使用Rxjs的工具函数 map filter
注意:Angular6
以后使用以前的rxjs
方法,必须安装 rxjs-compat
模块才可以使用 map
、filter
方法。
angular6
后官方使用的是 RXJS6
的新特性,所以官方给出了一个可以暂时延缓我们不需要修 改 rsjx
代码的办法
npm install rxjs-compat
import {Observable} from 'rxjs'; import 'rxjs/Rx';
let stream = new Observable < any > (observer = >{
let count = 0;
setInterval(() = >{
observer.next(count++);
},
1000);
});
stream.filter(val = >val % 2 == 0).subscribe(value = >console.log("filter>" + value));
stream.map(value = >{
return value * value
}).subscribe(value = >console.log("map>" + value));
8.6 Angualr6.x 以后 Rxjs6.x 的变化以及 使用
8.6.1 Rxjs 的变化参考
从Angular5
升级到Angular6
, angular6
相比较于angular5
总体变化不大,但是在RXJS
上面却有一些变动,下面给大家讲讲关于Angular6
版本升级和RXJS6
新特性的讲解
1. angular6 Angular7中使用以前的rxjs
对于写了半年多的项目,模块已经很多了,所以不可能在升级到angular6
后马上更新所有代码关于RXJS6
的新特性,所以官方给出了一个可以暂时延缓我们不需要修改rsjx
代码的办法。
npm install --save rxjs-compat
优点 : 暂时不用改代码,可以一点点地改,直到改完后吧这个包卸掉
缺点 : 对于rxjs6
的rename
的operator
无效,所以,如果有用到rename
的API
,必须手动修改
2. Angular6 以后 RXJS6的变化
RXJS6
改变了包的结构,主要变化在 import
方式和operator
上面以及使用pipe()
2.1 Imports 方式改变
从rxjs
中类似像导入observable
subject
等的不再进一步导入,而是止于rxjs
, rxjs6
在包的结构上进行了改变
2.2 operator的改变
总而言之: 类似于创建之类的用的API
都是从rxjs
引入的,类似于map
之类的操作都是从rxjs/operators
引入的
2.3 pipeable observable
2.4 被重新命名的API
RXJS6
改变了包的结构,主要变化在 import
方式和 operator
上面以及使用 pipe()
import {Observable} from 'rxjs';
import {map,filter} from 'rxjs/operators';
let stream= new Observable(observer => {
let count = 0;
setInterval(() = >{
observer.next(count++);
},
1000);
});
stream.pipe(filter(val = >val % 2 == 0))
.subscribe(value = >console.log("filter>" + value));
stream
.pipe(
filter(val = >val % 2 == 0),
map(value = >{
return value * value
}))
.subscribe(value = >console.log("map>" + value));
8.7 Rxjs 延迟执行
import {
Observable,
fromEvent
}
from 'rxjs';
import {
map,
filter,
throttleTime
}
from 'rxjs/operators';
var button = document.querySelector('button');
fromEvent(button, 'click')
.pipe(throttleTime(1000))
.subscribe(() = >console.log(`Clicked`));
九、Angular 中的数据交互(get jsonp post)
9.1 Angular get 请求数据
Angular5.x
以后 get
、post
和和服务器交互使用的是 HttpClientModule
模块。
1. 在 app.module.ts 中引入 HttpClientModule 并注入
import {HttpClientModule} from '@angular/common/http';
imports: [
BrowserModule,
HttpClientModule
]
2. 在用到的地方引入 HttpClient 并在构造函数声明
import {HttpClient} from "@angular/common/http";
constructor(public http:HttpClient) { }
3. get 请求数据
var api = "http://a.itying.com/api/productlist";
this.http.get(api).subscribe(response => {
console.log(response); });
9.2 Angular post 提交数据
Angular5.x
以后 get
、post
和和服务器交互使用的是HttpClientModule
模块。
1. 在 app.module.ts 中引入 HttpClientModule 并注入
import {HttpClientModule} from '@angular/common/http';
imports: [
BrowserModule,
HttpClientModule
]
2. 在用到的地方引入 HttpClient、HttpHeaders 并在构造函数声明 HttpClient
import {HttpClient,HttpHeaders} from "@angular/common/http";
constructor(public http:HttpClient) { }
post 提交数据
用express搭建一个server
// package.json { "dependencies": { "ejs": "^2.5.6", "express": "^4.15.3", "socket.io": "^2.0.3", "body-parser": "~1.17.1" } } // app.js 代码 var express = require('express'); var app=express(); var bodyParser = require('body-parser');
app.use(bodyParser.json()); app.use(bodyParser.urlencoded({ extended: false }));
/express允许跨域 / app.all('', function(req, res, next) { res.header("Access-Control-Allow-Origin", " "); res.header("Access-Control-Allow-Headers", "Content-Type,Content-Length, Authorization, Accept,X-Requested-With"); res.header("Access-Control-Allow-Methods","PUT,POST,GET,DELETE,OPTIONS"); res.header("X-Powered-By",' 3.2.1') if(req.method=="OPTIONS") res.send(200); else next(); });
//app.use(express.static(path.join(__dirname, 'public')));
app.get('/',function(req,res){ res.send('首页'); }) app.post('/dologin',function(req,res){ console.log(req.body); res.json({"msg":'post成功'}); })
app.get('/news',function(req,res){
//console.log(req.body);
res.jsonp({"msg":'这是新闻数据'});
})
app.listen(3000,'127.0.0.1',function(){ console.log('项目启动在3000端口') }); // angular代码
doLogin() {
// 手动设置请求类型 const httpOptions = { headers: new HttpHeaders({ 'Content-Type': 'application/json' }) }; var api = "http://127.0.0.1:3000/doLogin";
this.http.post(api, { username: '张三', age: '20' }, httpOptions).subscribe(response = >{ console.log(response); }); } 9.3 Angular Jsonp 请求数据
在 app.module.ts 中引入 HttpClientModule、HttpClientJsonpModule 并注入
import {HttpClientModule,HttpClientJsonpModule} from '@angular/common/http'; imports: [ BrowserModule, HttpClientModule, HttpClientJsonpModule ]
在用到的地方引入 HttpClient 并在构造函数声明
import {HttpClient} from "@angular/common/http";
constructor(public http:HttpClient) { }
jsonp 请求数据
// 接口支持jsonp请求 var api = "http://a.itying.com/api/productlist";
this.http.jsonp(api,'callback').subscribe(response => { console.log(response); }); 9.4 Angular 中使用第三方模块 axios 请求数据
安装 axios
cnpm install axios --save
用到的地方引入 axios
import axios from 'axios';
看文档使用
axios.get('/user?ID=12345').then(function(response) { // handle success console.log(response); }). catch(function(error) { // handle error console.log(error); }).then(function() { // always executed });
十、Angular 中的路由 10.1 Angular 创建一个默认带路由的项目
命令创建项目
ng new angualrdemo08 --skip-install
创建需要的组件
ng g component home ng g component news ng g component newscontent
找到 app-routing.module.ts 配置路由
// 引入组件
import { HomeComponent } from './home/home.component'; import { NewsComponent } from './news/news.component'; import { NewscontentComponent } from './newscontent/newscontent.component';
// 配置路由 const routes: Routes = [ {path: 'home', component: HomeComponent}, {path: 'news', component: NewsComponent}, {path: 'newscontent/:id', component: NewscontentComponent}, { path: '', redirectTo: '/home', pathMatch: 'full' } ];
找到 app.component.html 根组件模板,配置 router-outlet 显示动态加载的路由
10.2 routerLink 跳转页面 默认路由
首页 新闻 //匹配不到路由的时候加载的组件 或者跳转的路由
{
path: '
', / 任意的路由/
// component:HomeComponent
redirectTo:'home'
}
10.3 routerLinkActive 设置routerLink 默认选中路由
.active{
color:red;
}
10.4 routerLink Get传递参数
跳转
{{key}}--{{item}}
接收参数
import { ActivatedRoute } from '@angular/router';
constructor(public route:ActivatedRoute) { }
this.route.queryParams.subscribe((data)=>{ console.log(data); }) 10.5 动态路由 1.配置动态路由
const routes: Routes = [ {path: 'home', component: HomeComponent}, {path: 'news', component: NewsComponent}, {path: 'newscontent/:id', component: NewscontentComponent}, { path: '', redirectTo: '/home', pathMatch: 'full' } ];
跳转传值
跳转到详情 跳转到详情
获取动态路由的值
import { ActivatedRoute} from '@angular/router';
constructor( private route: ActivatedRoute) { }
ngOnInit() { console.log(this.route.params); this.route.params.subscribe(data=>this.id=data.id); } 10.6 动态路由的 js 跳转 // 引入 import { Router } from '@angular/router';
// 初始化
export class HomeComponent implements OnInit { constructor(private router: Router) {} ngOnInit() {} goNews(){ // this.router.navigate(['/news', hero.id]); this.router.navigate(['/news']); } } // 路由跳转 this.router.navigate(['/news', hero.id]); 10.7 路由 get 传值 js 跳转
引入 NavigationExtras
import { Router ,NavigationExtras} from '@angular/router';
定义一个 goNewsContent 方法执行跳转,用 NavigationExtras 配置传参。
goNewsContent() { let navigationExtras: NavigationExtras = { queryParams: { 'session_id': '123' }, fragment: 'anchor' }; this.router.navigate(['/news'], navigationExtras); }
获取 get 传值
constructor(private route: ActivatedRoute) { console.log(this.route.queryParams); } 10.8 父子路由
创建组件引入组件
import { NewsaddComponent } from './components/newsadd/newsadd.component'; import { NewslistComponent } from './components/newslist/newslist.component';
配置路由
{ path: 'news', component: NewsComponent, children: [{ path: 'newslist', component: NewslistComponent }, { path: 'newsadd', component: NewsaddComponent }] }
父组件中定义 router-outlet