安装 node.js
安装 cnpm
cnpm install -g @angular/cli
命令控制台中输入: ng v
如果出现以下画面,则安装成功
1. 在要创建项目的文件夹中打开 cmd 命令控制台
2. 创建项目
ng new 项目名称 (直接安装,安装的过程中自动执行 npm i,该命令安装依赖较慢,很有可能安装失败)
ng new 项目名称 --skip-install (建议, 跳过 npm i 安装,不会安装项目所需要的依赖)
安装过程中会选择是否安装路由,选择css预处理器等
3. 安装项目依赖
cnpm i (在项目根目录中执行)
4. 运行项目
ng serve --open (在项目根目录中执行)
根组件:
src/app/app.component.html
src/app/app.component.scss
src/app/app.component.ts
新建项目:
ng new 项目名称 (直接安装,安装的过程中自动执行 npm i,该命令安装依赖较慢,很有可能安装失败)
ng new 项目名称 --skip-install (建议, 跳过 npm i 安装,不会安装项目所需要的依赖)
运行项目: ng serve --open
创建一个组件: ng g component (文件路径/)组件名 注:执行该指令会自动在 app.module.ts 中引入并注册组件
创建一个服务: ng g service (文件路径/)服务名称
我们使用 {{ title }} 的形式将 ts 中的属性绑定到页面视图中
,我们使用 [] 将 ts 中的属性绑定到页面标签的属性中,页面标签的属性就可以动态改变了
ts中: public content:string = "我是一个h2标签
"
ts 中: public flage = false;
ts中: public pWidth:string = '50px'
{{ item }}
ts中: public list:any[] = ['111', 222 , '333']
{{1}}
{{2}}
{{3}}
ts 中: public option:number = 2;
public 共有类型(默认) 可以在这个类里面使用,也可以在类外面使用
protected 保护类型 只有在当前类和它的子类中使用
private 私有类型 只有在当前类才可以访问这个属性
Angular 中的管道其实就是过滤器,用来转换数据然后显示给用户
内置管道
管道 | 详情 |
---|---|
DatePipe | 根据区域设置规则格式化日期值 |
UpperCasePipe | 把文本转换成全大写形式 |
LowerCasePipe | 把文本转换成全小写形式 |
CurrencyPipe | 把数字转换成货币字符串,根据语言环境中的规则进行格式化 |
DecimalPipe | 把数字转换成带小数点的字符串,根据语言环境中的规则进行格式化 |
PercentPipe | 把数字转换成百分比字符串,根据语言环境中的规则进行格式化 |
AsyncPipe | 从一个异步回执中解出一个值 |
I18nPluralPipe | 将值映射到根据语言环境规则对该值进行复数化的字符串 |
I18nSelectPipe | 通用选择器,用于显示与当前值匹配的字符串 |
JsonPipe | 把一个值转换成 JSON 字符串格式。在调试时很有用 |
KeyValuePipe | 将 Object 或 Map 转换为键值对数组 |
SlicePipe | 从一个 Array 或 String 中创建其元素一个新子集(slice) |
TitleCasePipe | 把文本转换成标题形式。 把每个单词的第一个字母转成大写形式,并把单词的其余部分转成小写形式。 单词之间用任意空白字符进行分隔,比如空格、Tab 或换行符 |
具体用法详见 Angular 官方文档—管道
我们使用()将事件绑定到 ts 中
绑定点击事件
绑定表单事件
绑定键盘按下抬起事件:
将事件对象传入方法中:
ts 中: onKeyDown(e){
console.log(e)
}
MVVM 模式,只是针对表单
首先得在 app.module.ts 中引入 FormsModule 模块, 并注册
我们使用 [()] 的方式对表单元素进行双向绑定
public title:string = "vvv"
onKeyDown(e:any){
if(e.keyCode === 13){ // 13 是回车按键
console.log(this.title)
}
}
服务(service),所有组件都可以使用,通常我们把公共的方法放在服务里面,这样多有组件都能够共享这些方法
创建服务
ng g service (指定路径/)服务名称
编写服务代码
在 app.module.ts 中引用并配置服务
组件中使用服务
我们可以在服务中使用 localStorage 和 sessionStorage 来实现我们的数据持久化
// storage.service.ts
// localStorage 实例
import { Injectable } from '@angular/core';
@Injectable({
providedIn: 'root'
})
export class StorageService {
constructor() { }
save(key:string, val:any){
localStorage.setItem(key, JSON.stringify(val))
}
get(key:string){
let v = localStorage.getItem(key)
return v ? JSON.parse(v) : null
}
delete(key:string){
localStorage.removeItem(key)
}
}
原生 JS DOM操作:
let node:any = document.getElementById('box')
...
....
.....
Angular 提供的 DOM 操作(ViewChild):
1. 在模板中用 # 给 dom 起一个名字
我是一个节点
2. 在业务逻辑里面引入 ViewChild
import { Component, OnInit, ViewChild } from '@angular/core';
3. 通过 ViewChild 装饰器获取节点(写在类里面,构造函数上面)
@ViewChild('myBox') myBox:any
4. 在 ngAfterViewInit 生命周期函数中处理 dom
let node:any = this.myBox.nativeElement
node.style.color = 'red'
父组件中通过 ViewChild 获取子组件的属性以及调用子组件的方法
1. 用 # 给子组件取个名字
2. 在业务逻辑里面引入 ViewChild
import { Component, OnInit, ViewChild } from '@angular/core';
3. 通过 ViewChild 装饰器获取子组件(写在类里面,构造函数上面)
@ViewChild('header') header:any
4. 获取子组件的属性,调用子组件中的方法
console.loga(this.header.title)
this.header.run()
父组件定义一个属性 title,方法 run
public title:string = '我是父组件的 title'
run(){ console.log(111) }
在子组件中绑定父组件的属性
子组件中引入 Input 装饰器
import { Component, OnInit, Input} from '@angular/core'
子组件业务 ts 的类中通过 Input 装饰器获取父组件传过来的属性(写在构造函数的前面)
@Input() title:string
@Input() run:any
将整个父组件传递给子组件
子组件中引入 Input 装饰器
import { Component, OnInit, Input} from '@angular/core'
子组件通过 Input 装饰器获取整个父组件
@Input parent:any
子组件中获取父组件的属性,执行父组件的方法
console.log(this.parent.title)
this.parent.run()
见上一节 ViewChild 的用法
子组件中引入 Output 装饰器和 EventEmitter
import { Component, OnInit, Output, EventEmitter } from '@angular/core';
子组件中通过 Output 装饰器定义一个广播因子 outer,子组件触发自身方法将广播因子播散出去
@Output() private outer:any = new EventEmitter()
sentToParent(){
this.outer.emit('我是子组件的数据')
}
父组件中捕获广播因子,触发父组件自身的方法 run,$event 作为传递的参数
(子组件标签)
run(e:any){
console.log(e) // e: 我是子组件的数据
}
1. 可以通过服务的方式
2. 可以通过 localStorage 的方式
生命周期函数通常的讲就是组件创建、组件更新、组件销毁的时候会触发的一系列方法
注: 构造函数不是生命周期函数,构造函数中除了使用简单的值对局部变量进行初始化之外,什么都不应该做,构造函数在生命周期函数之前调用
组件内的生命周期函数
* ngOnChanges() 主要是用在父子组件传值中,父组件向子组件传值的时候 以及 父组件改变传值的数据的时候会触发该函数
* ngOnInit() 一般用来请求数据
ngDoCheck()
ngAfterContentInit() 组件渲染完成之后触发
ngAfterContentChecked()
* ngAfterViewInit() 在该函数中一般进行DOM操作
ngAfterViewChecked()
* ngOnDestroy() 组件销毁时触发该函数
Rxjs 是 ReactiveX 编程理念的JavaScript 版本。ReactiveX 来自微软,它是一种针对异步数据流的编程。简单的说,它将一切数据,包括 HTTP 请求,DOM 事件或者普通数据等包装成流的形式,然后用强大丰富的操作符对流进行处理,使我们能以同步编程的方式处理异步数据,并组合不同的操作符来轻松优雅的实现我们所需的功能。
Rxjs 是一种针对异步数据流编程工具,或者叫响应式扩展编程,目标就是异步编程。
// 引入 Observable
import { Observable } from 'rxjs'
// 实现方式
testRxjs(){
let stream = new Observable(observer=>{
setTimeout(()=>{
let name = 'cty'
observer.next(name) // 请求成功
// observer.error('失败') // 请求失败
}, 3000)
})
stream.subscribe((data)=>{
console.log(data)
})
}
Promise 的动作是无法撤回的,Rxjs 不一样,动作可以通过 unsbscribe() 方法中途撤回,而且 Observable 在内部做了智能的处理
testRxjs(){
let stream = new Observable(observer=>{
setTimeout(()=>{
let name = 'cty'
observer.next(name) // 请求成功
// observer.error('失败') // 请求失败
}, 3000)
})
let d = stream.subscribe((data)=>{
console.log(data)
})
// 一秒之后取消订阅,阻止方法的继续执行
setTimeout(() => {
d.unsubscribe()
console.log('已取消')
}, 1000);
}
这是 Promise 做不到的,对于 Promise 来说,最终结果要么 resolve 要么 reject,而且都只能触发一次
如果在同一个 Promise 对象上多次调用 resolve 方法
则会抛出异常。而 Observable 不一样,它可以不断触发下一个值
// 每隔一秒触发一次 console.log(data)
testRxjs(){
let stream = new Observable(observer=>{
setInterval(()=>{
let name = 'cty'
observer.next(name) // 请求成功
// observer.error('失败') // 请求失败
}, 1000)
})
stream.subscribe((data)=>{
console.log(data)
})
}
// 引入 Observable 和 相关的工具方法
import { Observable } from 'rxjs';
import { filter, map } from 'rxjs/operators';
testRxjs(){
let count:number = 0
let stream = new Observable(observer=>{
setInterval(()=>{
count++
observer.next(count) // 请求成功
// observer.error('失败') // 请求失败
}, 1000)
})
stream.pipe(
filter((val:any)=>val % 2 == 0),
map((val:any)=>Number(val)*2)
)
.subscribe( (data:any) => {
console.log(data)
})
}
// 输出 4 8 12 ...
① 在 app.module.ts 中引入 HttpClientModule 并注入
import { HttpClientModule } from '@angular/common/http'
...
imports: [ /* 配置当前模块运行依赖的其他模块 */
BrowserModule,
AppRoutingModule,
FormsModule,
HttpClientModule
],
② 在用到的地方引入 HttpClient 并在构造函数中声明
import { HttpClient } from '@angular/common/http'
...
constructor(public http:HttpClient) { }
③ 通过 this.http.get(api).subscribe((res)=>{console.log(res)}) 处理数据
getData(){
let api:any = 'http://a.itying.com/api/productlist'
this.http.get(api).subscribe((res:any)=>{
console.log(res)
})
}
① 在 app.module.ts 中引入 HttpClientModule 并注入
import { HttpClientModule } from '@angular/common/http'
...
imports: [ /* 配置当前模块运行依赖的其他模块 */
BrowserModule,
AppRoutingModule,
FormsModule,
HttpClientModule
],
② 在用到的地方引入 HttpClient 和 HttpHeaders 并在构造函数中声明 HttpClient
import { HttpClient, HttpHeaders } from '@angular/common/http'
...
constructor(public http:HttpClient) { }
③ 提交数据
doLogin(){
consthttpOptions:object = {
headers: new HttpHeaders({ 'Content-Type': 'application/json'})
}
let api:any = 'http://127.0.0.1:3000/dologin'
let data:any = {'username': 'cty', 'age': 20}
this.http.post(api, data, httpOptions).subscribe((response)=>{
console.log(response)
})
}
① 在 app.module.ts 中引入 HttpClientModule, HttpClientJsonpModule 并注入
import { HttpClientModule, HttpClientJsonpModule } from '@angular/common/http'
...
imports: [ /* 配置当前模块运行依赖的其他模块 */
BrowserModule,
AppRoutingModule,
FormsModule,
HttpClientModule,
HttpClientJsonpModule
],
② 在用到的地方引入 HttpClient 和 HttpHeaders 并在构造函数中声明 HttpClient
import { HttpClient } from '@angular/common/http'
...
constructor(public http:HttpClient) { }
③ 提交数据
getJsonpData(){
// jsonp请求,服务器必须得支持jsonp
/*
'http://a.itying.com/api/productlist?callback=xxx'
'http://a.itying.com/api/productlist?cb=xxx'
*/
let api:any = 'http://a.itying.com/api/productlist'
this.http.jsonp(api, 'callback').subscribe((res:any)=>{
console.log(res)
})
}
① 安装 axios 模块
npm install axios
② 引入 axios 模块
import axios from 'axios'
③ 使用 axios
getAxiosData(){
let api:any = 'http://a.itying.com/api/productlist'
let stream:any = new Observable(subscriber=>{
axios.get(api).then((res:any)=>{
subscriber.next(res)
}).catch((err)=>{
subscriber.error('err')
})
})
stream.subscribe((res:any)=>{
console.log(res)
})
}
路由就是根据不同的 url 地址,动态的让根组件挂载其他组件来实现一个单页面应用
① 在 app-routing.module.ts 中引入组件,并编写路由路径以及路由路径对应的组件
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
// 引入组件
import { HomeComponent } from './components/home/home.component';
import { NewsComponent } from './components/news/news.component';
// 编写路由路径以及路径对应的组件
const routes: Routes = [
{path: 'home', component: HomeComponent},
{path: 'news', component: NewsComponent},
// 匹配不到路由的时候加载的组件 或者跳转的路由 默认的路由
{path: '**', redirectTo: 'home'}
];
@NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule]
})
export class AppRoutingModule { }
② 路由组件将动态加载到 中,通过 routerLink 映射到对应的路由中
routerLinkActive="active" 调整路由选种样式 (.active{ color : red; })
<header class="header">
<a [routerLink]="['/home']" routerLinkActive="active">Home</a>
<button routerLink='/news' routerLinkActive="active">News</button>
</header>
<router-outlet></router-outlet>
① 跳转时添加 [queryParams]="参数"
<div [routerLink]="['/home']" routerLinkActive="active" [queryParams]="{aid: 'home'}">Home</div>
<div routerLink='/news' routerLinkActive="active" [queryParams]="{cty: 'news'}">News</div>
② 组件接收
// 引入 ActivatedRoute 模块
import { ActivatedRoute } from '@angular/router';
...
// 构造函数中声明 route
constructor(public route: ActivatedRoute) {}
...
// 方法中获取 传过来的参数
this.route.queryParams.subscribe((params:any)=>{
console.log(params)
})
① 配置动态路由,在路径后面添加参数,参数为 cty
const routes: Routes = [
{path: 'home', component: HomeComponent},
{path: 'news/:cty', component: NewsComponent},
// 匹配不到路由的时候加载的组件 或者跳转的路由 默认的路由
{path: '**', redirectTo: 'home'}
];
② 跳转时给参数赋值 ,'333' 就是参数值
<div [routerLink]="['/news/', '3333']" routerLinkActive="active">News</div>
③ 组件接收
// 引入 ActivatedRoute 模块
import { ActivatedRoute } from '@angular/router';
...
// 构造函数中声明 route
constructor(public route: ActivatedRoute) {}
...
// 方法中获取 传过来的参数
this.route.params.subscribe((data:any)=>{
console.log(data)
})
① 引入 Router,NavigationExtras模块
import { Router, NavigationExtras } from '@angular/router';
② 在构造函数中声明 router
constructor(public router: Router) { }
③ 跳转路由
let queryParams: NavigationExtras = {
queryParams: {cty: 25}
}
this.router.navigate(['/home/'], queryParams)
① 引入 Router 模块
import { Router } from '@angular/router';
② 在构造函数中声明 router
constructor(public router: Router) { }
③ 跳转路由
// 数组中第二个元素是动态路由的参数值,普通路由不用写第二个元素
this.router.navigate(['/news/', '12333'])
this.router.navigate(['/home'])
① 在 app-routing.module.ts 中配置父子路由
const routes: Routes = [
{
path: 'home',
component: HomeComponent,
children: [
{
path: 'setting',
component: SettingComponent,
},
{
path: 'more',
component: MoreComponent,
},
// 配置默认路由
{path: '**', redirectTo: 'setting'}
]
},
{path: 'news/:cty', component: NewsComponent},
// 匹配不到路由的时候加载的组件 或者跳转的路由 默认的路由
{path: '**', redirectTo: 'home'}
];
② 模板中编写路由跳转 以及映射路由
<div class="home">
<div class="left">
<div class="left-route" [routerLink]="['/home/setting']">设置</div>
<div class="left-route" [routerLink]="['/home/more']">更多</div>
</div>
<div class="right">
<router-outlet></router-outlet>
</div>
</div>