一、环境搭建篇
1.准备工作
1.1安装node
安装angular的计算机上面必须安装最新的nodejs–注意安装nodejs稳定版本
1.2选择一个 命令工具, npm, cnpm, yarn ,任选其一
2.安装Angular CLI
2.1安装typescript(可选)
$ npm install -g typescript
新建项目会局部安装,所以该步骤不必须。
2.2全局安装Angular CLI
$ yarn global add @angular/cli 或者 npm install @angular/cli -g
此步骤会安装最新版Angular CLI,如果需要指定版本可使用
$ npm install @angular/cli@12.2.18 -g
2.3检测是否安装成功
$ ng version 或者 ng v
3.创建Angular项目
3.1新建项目
ng new ng-Demo
3.2启动项目
进入项目文件夹
cd my-app
启动服务
npm start或ng serve
如果我们需要自动打开浏览器,添加–open(或-o)
其它参数参考:
–dry-run: boolean, 默认为 false, 若设置 dry-run 则不会创建任何文件
–verbose: boolean, 默认为 false
–link-cli: boolean, 默认为 false, 自动链接到 @angular/cli 包
–skip-install: boolean, 默认为 false, 表示跳过 npm install
–skip-git: boolean, 默认为 false, 表示该目录不初始化为 git 仓库
–skip-tests: boolean, 默认为 false, 表示不创建 tests 相关文件
–skip-commit: boolean, 默认为 false, 表示不进行初始提交
–directory: string, 用于设置创建的目录名,默认与应用程序的同名
–source-dir: string, 默认为 ‘src’, 用于设置源文件目录的名称
–style: string, 默认为 ‘css’, 用于设置选用的样式语法 (‘css’, ‘less’ or ‘scss’)
–prefix: string, 默认为 ‘app’, 用于设置创建新组件时,组件选择器使用的前缀
–mobile: boolean, 默认为 false,表示是否生成 Progressive Web App 应用程序
–routing: boolean, 默认为 false, 表示新增带有路由信息的模块,并添加到根模块中
–inline-style: boolean, 默认为 false, 表示当创建新的应用程序时,使用内联样式
–inline-template: boolean, 默认为 false, 表示当创建新的应用程序时,使用内联模板
4.简单使用
4.1创建组件
ng generate component news(简写:ng g component news)
4.2其他命令
Angualr CLI提供了许多常用命令供我们选择:
ng generate class my-new-class // 新建类, 新建一个名为my-new-class的类 (class)
ng generate component my-new-component // 新建组件
ng generate directive my-new-directive // 新建指令
ng generate enum my-new-enum // 新建枚举
ng generate module my-new-module // 新建模块
ng generate pipe my-new-pipe // 新建管道
ng generate service my-new-service // 新建服务
当然选择。。简写:
ng g cl my-new-class // 新建 class
ng g c my-new-component // 新建组件
ng g d my-new-directive // 新建指令
ng g e my-new-enum // 新建枚举
ng g m my-new-module // 新建模块
ng g p my-new-pipe // 新建管道
ng g s my-new-service // 新建服务
二、基础语法篇
1.绑定数据
1.1文本绑定
<h2>{{title}}</h2>
<h3>{{titlename}}</h3>
1.2属性绑定
<!-- 绑定属性 -->
<div [title]="title">{{message}}</div>
1.3Html绑定
<!-- 解析html -->
<span [innerHTML]="content"></span>
2.数据循环 *ngFor
2.1普通循环
<!-- 循环 -->
<ul>
<li *ngFor="let item of list">{{item}}</li>
</ul>
2.1循环设置key
<!-- 循环 -->
<ul>
<li *ngFor="let item of list;let key = index">{{key}}--{{item}}</li>
</ul>
3.条件判断*ngIf *ngSwitch
3.1*ngIf
<!-- 条件判断语句ngIf -->
<h1 *ngIf="flag">我是true</h1>
<h1 *ngIf="!flag">我是false</h1>
3.2*ngSwitch
<!-- 条件判断语句ngSwitch -->
<div [ngSwitch]="orderStatus">
<div *ngSwitchCase="1">已经支付</div>
<div *ngSwitchCase="2">已发货</div>
<div *ngSwitchCase="3">已签收</div>
<div *ngSwitchCase="4">已完成</div>
</div>
4.属性绑定 ngClass ngStyle
4.1ngClass
<!-- 属性[ngClass] -->
<div [class]="{'blue':true,'red':false}">class绑定</div>
4.2ngStyle
<!-- 绑定行内样式 -->
<div [ngStyle]="{color:'red'}">我是行内样式</div>
5.管道
<!-- 管道 -->
<div>{{today | date:'YYYY-MM-dd hh:mm:ss'}}</div>
6.执行事件
6.1click
<!-- 点击事件 -->
<button (click)="run($event)">按钮</button>
6.2keydown
<!-- 鼠标按下事件 -->
<input type="text" (keydown)="keyDown($event)">
7.双向数据绑定–MVVM 只是针对表单
7.1 在app.module.ts导入FormsModule
7.2 并在imports导入
import { FormsModule } from '@angular/forms';
....
imports: [ /*配置当前模块运行依赖的其他模块*/
FormsModule
],
7.3在页面中使用
<!-- 双向绑定 -->
<h1 >{{message}}</h1>
<button (click)="setmessage()">改变</button>
<input type="text" [(ngModel)]="message" />
8.home.component.ts
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'app-home',
templateUrl: './home.component.html',
styleUrls: ['./home.component.scss']
})
export class HomeComponent implements OnInit {
title: string = 'ngDemo';
message: any = '我是home组件'
today: any = new Date();
constructor() { }
// 生命周期
ngOnInit(): void {
}
// HTML内容
public content = '<h2>我是一个h2标签</h2>'
// 数组
public list: any[] = ['苹果', '香蕉', '西瓜']
// 图片地址
public picUrl: string = 'https://tse3-mm.cn.bing.net/th/id/OIP-C.e_x8-Ry-rp2r6PHr-ZSSVgHaEK?w=291&h=180&c=7&r=0&o=5&dpr=1.3&pid=1.7'
// 判断条件
public flag: boolean = true
// 订单状态
public orderStatus: number = 3
// 事件
run(event: any) {
var target = event.target
console.log(target);
this.flag = !this.flag
}
keyDown(event: any) {
if (event.keyCode === 13) {
console.log('你按下了回车键');
console.log(event.target.value);
}
}
setmessage() {
console.log(this.message);
this.message = '改变后'
}
}
三、组件通信篇
1.原生js进行DOM操作
组件HTML:
<div class="content">
<p>内容区域</p>
<div id="box">
this is box
</div>
<br>
<div id="box1" *ngIf="flag">
this is box1
</div>
<button (click)="showAside()">弹出侧边栏</button>
<button (click)="hideAside()">隐藏侧边栏</button>
</div>
<aside id="aside">
这是一个侧边栏
</aside>
组件ts:
public flag:boolean=true;
constructor() { }
ngOnInit(): void {
//组件和指令初始化完成 并不是真正的dom加载完成
let oBox:any=document.getElementById('box');
console.log(oBox.innerHTML);
oBox.style.color="red";
//获取不到dom节点
/*
let oBox1:any=document.getElementById('box1');
console.log(oBox1.innerHTML);
oBox1.style.color="blue";
*/
}
//视图加载完成以后触发的方法 dom加载完成 (建议把dom操作放在这个里面)
ngAfterViewInit(){
let oBox1:any=document.getElementById('box1');
console.log(oBox1.innerHTML);
oBox1.style.color="blue";
}
showAside(){
//原生js获取dom节点
var asideDom:any=document.getElementById('aside');
asideDom.style.transform="translate(0,0)";
}
hideAside(){
//原生js获取dom节点
var asideDom:any=document.getElementById('aside');
asideDom.style.transform="translate(100%,0)";
}
2.Angular中进行DOM操作
2.1组件模版定义属性(使用#)
<div #ngbox>我是ngbox</div>
2.2组件ts通过ViewChild 获取dom
3.父组件获取子组件的数据和方法
父组件:news
子组件:header
3.1在父组件中调用子组件并使用#定义一个名称
<app-header #header ></app-header>
3.2在父组件引入ViewChild
import { Component, OnInit,ViewChild } from '@angular/core';
3.3利用属性装饰器ViewChild 和刚才的子组件关联起来
@ViewChild('header')
public header: any;
3.4调用子组件
getChildProp() {
console.log(this.header);
}
3.5news.component.ts
import { Component, OnInit,ViewChild } from '@angular/core';
@Component({
selector: 'app-news',
templateUrl: './news.component.html',
styleUrls: ['./news.component.scss']
})
export class NewsComponent implements OnInit {
@ViewChild('ngbox')
public ngbox: any;
@ViewChild('header')
public header: any;
constructor() { }
title: any = '我是新闻'
ngOnInit(): void {
console.log("ngbox",this.ngbox);
}
ngAfterViewInit(): void {
console.log("ngbox",this.ngbox);
}
getChildProp() {
console.log(this.header);
}
}
4.父组件给子组件传值
父组件:news
子组件:header
4.1父组件调用子组件的时候传入数据
<app-header [titlename]="title"></app-header>
4.2子组件引入 Input 模块
import { Component, OnInit ,Input } from '@angular/core';
4.3子组件中 @Input 接收父组件传过来的数据
4.4子组件中使用父组件的数据
<h3>{{titlename}}</h3>
5.子组件通过@Output触发父组件的方法
5.1子组件引入 Output 和 EventEmitter
import { Component, OnInit ,Input,Output,EventEmitter} from '@angular/core';
5.2子组件中实例化 EventEmitter
@Output() public send = new EventEmitter();
5.3子组件通过 EventEmitter 对象 send实例广播数据
sendName() {
this.send.emit(this.message)
}
5.4父组件调用子组件的时候,定义接收事件 ,send就是子组件的 EventEmitter 对象 send
<app-header [hometitle]="title" [homeWork]="homeWork" (send)="send($event)"></app-header>
5.5父组件接收到数据会调用自己的 send方法,这个时候就能拿到子组件的数据
send(ev:any) {
console.log(ev);
}
6.非父子组件通讯
1、公共的服务
2、Localstorage(推荐)
3、Cookie
四、生命周期篇
官方定义:https://angular.cn/guide/lifecycle-hooks
1.ngOnChanges()
当 Angular(重新)设置数据绑定输入属性时响应。 该 方法接受当前和上一属性值的 SimpleChanges 对象 当被绑定的输入属性的值发生变化时调用,首次调用一 定会发生在 ngOnInit() 之前。
当被绑定的输入属性的值发生变化时调用(父子组件传值的时候会触发)
2.ngOnInit()
在 Angular 第一次显示数据绑定和设置指令/组件的输 入属性之后,初始化指令/组件。 在第一轮 ngOnChanges() 完成之后调用,只调用一次。
使用 ngOnInit() 有两个原因:
1、在构造函数之后马上执行复杂的初始化逻辑
2、在 Angular 设置完输入属性之后,对该组件进行准备。 有经验的开发者会认同组件的构建应该很便宜和安全。
请求数据一般放在这个里面
3.ngDoCheck()
检测,并在发生 Angular 无法或不愿意自己检测的变 化时作出反应。在每个 Angular 变更检测周期中调用, ngOnChanges() 和 ngOnInit()之后。
检测,并在发生 Angular 无法或不愿意自己检测的变化时作出反应
4.ngAfterContentInit()
当把内容投影进组件之后调用。第一次 ngDoCheck() 之 后调用,只调用一次。
当把内容投影进组件之后调用
5.ngAfterContentChecked()
每次完成被投影组件内容的变更检测之后调用。 ngAfterContentInit() 和每次 ngDoCheck() 之后调用。
6.ngAfterViewInit()
初始化完组件视图及其子视图之后调用。第一次 ngAfterContentChecked()之后调用,只调用一次。
初始化完组件视图及其子视图之后调用(dom操作放在这个里面)
7.ngAfterViewChecked()
每次做完组件视图和子视图的变更检测之后调用。 ngAfterViewInit()和每次 ngAfterContentChecked() 之后 调用
8.ngOnDestroy()
当 Angular 每次销毁指令/组件之前调用并清扫。在这 儿反订阅可观察对象和分离事件处理器,以防内存泄 漏。 在 Angular 销毁指令/组件之前调用。
五、异步请求篇
演示组件:ngdemo\src\app\async\http
演示服务: ngdemo\src\app\services\request.service.ts
1.Angular get 请求数据
1.1在 app.module.ts 中引入 HttpClientModule 并注入
import {HttpClientModule} from '@angular/common/http'
imports: [ BrowserModule, HttpClientModule ]
1.2在用到的地方引入 HttpClient 并在构造函数声明
import {HttpClient} from "@angular/common/http";
constructor(public http:HttpClient) { }
1.3get 请求数据
var api = "http://localhost:3000/info";
this.http.get(api).subscribe(response => {
console.log(response);
});
1.4http.component.ts
import { Component, OnInit } from '@angular/core';
import { HttpClient, HttpHeaders, HttpParams } from '@angular/common/http';
// const Qs = require('qs');
import Qs from 'qs';
@Component({
selector: 'app-http',
templateUrl: './http.component.html',
styleUrls: ['./http.component.less']
})
export class HttpComponent implements OnInit {
constructor(public http: HttpClient) { }
ngOnInit(): void {
this.getGetdata();
// this.getPostdata()
}
getGetdata() {
var parmas = { username: 'admin' }
var newparmas = Qs.stringify(parmas)
console.log(newparmas);
// 方法1
// this.http.get(' http://localhost:3000/users?username=admin').subscribe((response) => {
// console.log(response);
// })
// 方法2
// this.http.get(' http://localhost:3000/users?' + newparmas).subscribe((response) => {
// console.log(response);
// })
// 方法3
// var newparmas2 = new HttpParams({ fromString: newparmas })
// this.http.get(' http://localhost:3000/users', {
// params:newparmas2
// }).subscribe((response) => {
// console.log(response);
// })
}
// getPostdata() {
// this.http.post(' http://localhost:3000/users',{name:'小刘'},{new Httpheaders:{'Content-Type':'application/json'}}).subscribe((response) => {
// console.log(response);
// })
// }
}
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) { }
3.post 提交数据
const httpOptions={
headers:newHttpHeaders({‘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);
});
3. Angular 中使用第三方模块 axios 请求数据
1.安装 axios
yarn add axios -D
2、用到的地方引入 axios
import axios from ‘axios’;
3、看文档使用
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
});
4.封装Rxjs
1.rxjs.service.ts
import { Injectable } from '@angular/core'; import { HttpClient, HttpHeaders } from '@angular/common/http'; import { environment } from 'src/environments/environment'; import Qs from 'qs'
@Injectable({ providedIn: 'root' }) export class RxjsService {
constructor(public http:HttpClient) { } getFun(url: string, data: any) {
var params = Qs.stringify(data);
return this.http.get(environment.baseURL + url + "?" + params); } postFun(url: string, data: any) {
return this.http.post(environment.baseURL + url, data, {
headers:new HttpHeaders ({
'Content-Type': 'application/json' // 默认值
})}) } }
5.封装axios
1.下载axios
npm i axios
2.axios.service.ts
import { Injectable } from '@angular/core';
import axios from 'axios';
import Qs from 'qs'
axios.defaults.timeout = 5000
import { environment } from 'src/environments/environment';
console.log(environment.baseURL,'base');
// 请求基础路径
axios.defaults.baseURL = environment.baseURL
//http request 拦截器 客户端给服务端 的数据
axios.interceptors.request.use(
config => {
config.headers.common = {
'X-API-TOKEN': localStorage.getItem('token'),
'version': '1.0',
'Content-Type': 'application/json;charset=UTF-8'
}
if (config.data && config.data.form) {
//转化成formData格式
// transformRequest只能用在 PUT, POST, PATCH 这几个请求方法
config.transformRequest = [function (data) {
//方式一
delete data.form;
// var ret = '';
// for (let it in data) {
// ret += it+'=' +data[it] + '&';
// }
// return ret.substring(0,ret.length-1);
//方式二:
var test2 = Qs.stringify(data);
return test2;
}]
}
return config;
},
error => {
return Promise.reject();
}
);
//响应拦截器即异常处理 - -- 服务给客户端的数据进行处理
axios.interceptors.response.use(response => {
if (response.status >= 200 && response.status < 300) {
return Promise.resolve(response);
} else {
return Promise.reject(response);
}
// return response
}, err => {
if (err && err.response) {
var errorMsg = '';
switch (err.response.status) {
case 400:
console.log('错误请求')
errorMsg = '错误请求';
break;
case 401:
console.log('未授权,请重新登录');
errorMsg = '未授权,请重新登录';
break;
case 403:
console.log('拒绝访问')
errorMsg = '拒绝访问';
break;
case 404:
console.log('请求错误,未找到该资源')
errorMsg = '请求错误,未找到该资源';
break;
case 405:
console.log('请求方法未允许')
errorMsg = '请求方法未允许';
break;
case 408:
console.log('请求超时')
errorMsg = '请求超时';
break;
case 500:
console.log('服务器端出错')
errorMsg = '服务器端出错';
break;
case 501:
console.log('网络未实现')
errorMsg = '请求方法未允许';
break;
case 502:
console.log('网络错误')
errorMsg = '网络错误';
break;
case 503:
console.log('服务不可用')
errorMsg = '服务不可用';
break;
case 504:
console.log('网络超时')
errorMsg = '网络超时';
break;
case 505:
console.log('http版本不支持该请求')
errorMsg = 'http版本不支持该请求';
break;
default:
console.log(`连接错误${err.response.status}`)
}
alert(errorMsg);
} else {
console.log('连接到服务器失败')
}
return Promise.resolve(err.response)
})
@Injectable({
providedIn: 'root'
})
export class AxiosService {
constructor() { }
// 包装一个 直接利用axios自身是一个Promise对象的思想封装
postFun(url: string, data: any) {
return new Promise((resolve, reject) => {
axios.post(url, data)
.then(response => {
resolve(response);
})
})
}
getFun(url: string, params = {}) {
return new Promise((resolve, reject) => {
axios.get(url, {
params: params
})
.then(response => {
resolve(response.data);
})
})
}
}
六、路由配置篇
1.配置默认路由
<a routerLink="/home">首页</a>
<a routerLink="/news">新闻</a>
//匹配不到路由的时候加载的组件 或者跳转的路由
{
path: '**', /*任意的路由*/
// component:HomeComponent
redirectTo:'home'
}
2.Angular routerLinkActive 设置 routerLink 默认选中路由
方法一:
<h1>
<a routerLink="/home" routerLinkActive="active">
首页
</a>
<a routerLink="/news" routerLinkActive="active">
新闻
</a>
</h1>
方法二:
<h1>
<a [routerLink]="[ '/home' ]" routerLinkActive="active">首页</a>
<a [routerLink]="[ '/news' ]" routerLinkActive="active">新闻</a>
</h1>
3.动态路由
1.问号传参
问号传参的url地址显示为 …/list-item?id=1
方法一:
<a [routerLink]="['/list-item']" [queryParams]="{id:item.id}">
<span>{{ item.name }}</span>
</a>
方法二:
import { Router } from '@angular/router';
constructor(private router: Router) {}
this.router.navigate(['/newscontent'],{
queryParams:{
name:'laney',
id:id
},
skipLocationChange: true
//可以不写,默认为false,设为true时路由跳转浏览器中的url会保持不变,传入的参数依然有效
});
获取参数:
import { ActivatedRoute } from '@angular/router';
constructor(public route:ActivatedRoute) { }
ngOnInit() {
this.route.queryParams.subscribe((data)=>{
console.log(data);
})
}
2.路径传参
路径传参的url地址显示为 …/list-item/1
方法一:
<a [routerLink]="['/list-item', item.id]">
<span>{{ item.name }}</span>
</a>
方法二:
this.router.navigate(['/list-item', item.id]);
路径配置:
{path: 'list-item/:id', component: ListItemComponent}
获取参数方式:
this.route.params.subscribe(
param => {
this.id= param['id'];
}
)
4.父子路由
1.创建组件引入组件
import { WelcomeComponent } from './components/home/welcome/welcome.component';
import { SettingComponent } from './components/home/setting/setting.component';
2.配置路由
{
path:'home',
component:HomeComponent,
children:[{
path:'welcome',
component:WelcomeComponent
},{
path:'setting',
component:SettingComponent
},
{path: '**', redirectTo: 'welcome'}
]
},
3.父组件中定义router-outlet
<router-outlet></router-outlet>