HttpClient对http协议支持非常好,使用起来很简单,版本更新快,功能也很强大,具有足够的灵活性和扩展性。
基本了解
概念
- HttpClient作用是发送Http请求。
- HttpClient在内部封装了浏览器提供的 XMLHttpRequest 接口。
- HttpClient使用基于可观察(Observable)对象的 API 。
- HttpClient提供了请求和响应拦截器。
- HttpClient流式错误处理机制。
使用
-
在跟模块
app.module.ts
导入HttpClient, 并在@NgModule
导出HttpClientModule
。// 导入HttpClient import { HttpClientModule } from '@angular/common/http'; @NgModule({ // 导出 imports: [ .... HttpClientModule ] })
-
在
assets
文件夹下新建文件todos.json
{ "name": "TODOS", "desc": "TODOS desc" }
-
在组件
app.component.ts
中导入HttpClient,并且要在constructor
中创建一个http的属性。// 导入HttpClient import { HttpClient } from '@angular/common/http' .... export class AppComponent { // 告知组件要使用HttpClient constructor(private http: HttpClient) { } name: string getData() { // 发送get请求 第一个参数是要请求的文件 通过subscribe函数去订阅一个方法 将参数传递过来。 this.http.get('../assets/todos.json').subscribe(res => { console.log(res) }) } }
app.component.html
页面样式<div> <button (click)="getData()">获取数据</button> <h1>通过 HttpClient 获取到的数据为:</h1> </div>
这样控制台可以打印出来,json文件中的数据。
app.component.ts
.... export class AppComponent { .... name: string getData() { // 要记得给res约束类型 this.http.get('../assets/todos.json').subscribe((res: any) => { console.log(res) this.name = res.name }) } }
添加类型检查
设置接口
interface Todo {
name: string,
desc: string
}
- 在对
http
添加泛型,res
就不要添加类型提示了。而且在this.name = res.name
后面的res
会有只能提示,更加方便。
export class AppComponent {
....
getData() {
// 添加<Todo>的类型检查 res后面的any就可以去掉了。
this.http.get<Todo>('../assets/todos.json').subscribe((res) => {
this.name = res.name
})
}
}
- 可以在
res
后面添加类型约束
export class AppComponent {
...
getData() {
this.http.get('../assets/todos.json').subscribe((res: TOdo) => {
this.name = res.name
})
}
}
- 最好的办法就是两者同时使用
export class AppComponent {
.....
getData() {
this.http.get<Todo>('../assets/todos.json').subscribe((res: TOdo) => {
this.name = res.name
})
}
}
获取一个完整的响应
...
export class AppComponent {
.....
getData() {
this.http
.get('../assets/todos.json',{ observe: 'response' })
.subscribe((res)=> {
console.log(res.headers.get('content-type'), res.body)
})
}
}
就可以拿到响应头以及响应体,控制台会显示
application/json; charset=UTF-8
{name: "TODOS", desc: "TODOS desc"}
desc: "TODOS desc"
name: "TODOS"
__proto__: Object
....
添加类型检查 HttpResponse
- 添加泛型
...
export class AppComponent {
.....
getData() {
this.http
// 添加泛型
.get<Todo>('../assets/todos.json', { observe: 'response' })
.subscribe(res => {
console.log(res.headers.get('content-type'), res.body)
})
}
}
- 可以在
res
后面添加HttpResponse类型约束
import { HttpClient, HttpResponse } from '@angular/common/http';
...
export class AppComponent {
.....
getData() {
this.http
// 引入HttpResponse 添加泛型
.get('../assets/todos.json', { observe: 'response' })
.subscribe((res: HttpResponse<Todo>) => {
console.log(res.headers.get('content-type'), res.body)
})
}
}
- 最好的使用方式是将两者相结合。
import { HttpClient, HttpResponse } from '@angular/common/http';
...
export class AppComponent {
.....
getData(){
this.http
.get<Todo>('../assets/todos.json', { observe: 'response' })
.subscribe((res: HttpResponse<Todo>) => {
console.log(res.headers.get('content-type'), res.body)
this.name = res.body.name
})
}
}
处理错误
使用subscribe
中的第二个参数来进行错误处理
...
export class AppComponent {
.....
getData(){
this.http
.get<Todo>('../assets/todos.json', { observe: 'response' })
.subscribe(
(res: HttpResponse<Todo>) => {
console.log(res.headers.get('content-type'), res.body)
this.name = res.body.name
}),
// 错误处理
err => {
console.log(err)
}
}
}
json-server提供接口
-
安装
$ npm i -g json-server
-
创建一个json文件
db.json
{ "todos": [ { "id": 1, "name": "eat", "done": false }, { "id": 2, "name": "sleep", "done": true }, { "id": 3, "name": "learn", "done": false } ] }
-
运行
json-server
$ json-server db.json
控制台显示
➜ json-server db.json \{^_^}/ hi! Loading db.json Done Resources http://localhost:3000/todos Home http://localhost:3000 Type s + enter at any time to create a snapshot of the database
-
在浏览器打开
http://localhost:3000/todos
就可以得到一个json-server
的接口
发送rest请求
app.component.html
<div>
<h1>发送请求 GET/POST/DELETE/PATCH</h1>
<button (click)="getData()">(查询)GET请求</button>
<br>
<button (click)="addData()">(添加)POST请求</button>
<br>
<button (click)="delData()">(删除)DELETE请求</button>
<br>
<button (click)="updateData()">(修改)PATCH请求</button>
</div>
app.component.ts
import { Component } from '@angular/core';
import { HttpClient, HttpResponse } from '@angular/common/http';
interface Todo {
name: string,
desc: string
}
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
constructor(private http: HttpClient) {}
// 发送请求 GET/POST/DELETE/PATCH
// REST API
// 接口地址
url = 'http://localhost:3000/todos'
getData() {
this.http
.get(this.url)
.subscribe(res=>{
console.log('getData==>', res)
})
}
addData() {
this.http
.post(this.url, {
"name": "add",
"done": "false"
})
.subscribe(res => {
console.log('addData==>',res)
})
}
delData() {
this.http
.delete(`${this.url}/2`)
.subscribe(res => {
console.log('delData==>', res)
})
}
updateData() {
this.http
.patch(`${this.url}/3`, {
name: '好好学习 天天向上'
})
.subscribe(res => {
console.log('updateData==>', res)
})
}
}
HttpClient升级todos案例
先要在app.module.ts
中导入导出HttpClient
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { AppComponent } from './app.component';
import { TodosModule } from './todos/todos.module';
// 导入 HttpClientModule
import { HttpClientModule } from '@angular/common/http';
@NgModule({
declarations: [
AppComponent
],
imports: [
BrowserModule,
TodosModule,
// 导出 HttpClientModule
HttpClientModule
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
1. 获取数据
先在todos.service.ts
导入HttpClient
// 导入HttpClient
import { HttpClient } from '@angular/common/http';
.....
export class TodosService {
// 告知组件 在组件中使用HttpClient
constructor(private http: HttpClient) {}
...
}
启用todos
的json-server
➜ json-server db.json
\{^_^}/ hi!
Loading db.json
Done
Resources
http://localhost:3000/todos
Home
http://localhost:3000
Type s + enter at any time to create a snapshot of the database
添加Todos的接口地址,将之前的假数据去掉;在服务中不做任何的处理,直接返回。
export class TodosService {
...
todosUrl = 'http://localhost:3000/todos';
// 提供数据
getTodos() {
// const todos: Todo[] = [
// {id: 1, name: '1', done: true},
// {id: 2, name: '2', done: false}
// ]
// // 添加一个初终数据
// this.todos = todos
// return todos
return this.http.get(this.todosUrl)
}
...
}
在组件todo.component.ts
中使用到数据,在进行处理。在ngOnInit
这个里面调用subscribe
。
.....
export class TodoComponent implements OnInit {
....
ngOnInit() {
this.TodosService
.getTodos()
.subscribe((todos: Todo[]) => {
this.todos = [...todos]
console.log("Todos组件中获取到的数据-->",todos)
})
}
}
添加一下类型检查
todos.service.ts
export class TodosService {
...
todosUrl = 'http://localhost:3000/todos';
// 提供数据
getTodos() {
// 泛型 <Todo[]>
return this.http.get<Todo[]>(this.todosUrl)
}
...
}
todo.component.ts
.....
export class TodoComponent implements OnInit {
....
ngOnInit() {
this.TodosService
.getTodos()
// 约束 Todo[]
.subscribe((todos: Todo[]) => {
this.todos = todos
console.log("Todos组件中获取到的数据-->",todos)
})
}
}
2. 添加数据
todos.service.ts
export class TodosService {
...
todosUrl = 'http://localhost:3000/todos';
// 添加任务
addTodo(todoName: string) {
// let id: number
// if (this.todos.length === 0) {
// return 1
// } else {
// id = this.todos[this.todos.length - 1 ].id - 1
// }
// this.todos.push({
// id,
// name: todoName,
// done: false,
// })
// post<Todo> 泛型
return this.http.post<Todo>(this.todosUrl, {
"name": todoName,
"done": true
})
}
...
}
todo.component.ts
.....
export class TodoComponent implements OnInit {
....
addTodo(todoName: string) {
// this.TodosService.addTodo(todoName)
this.TodosService
.addTodo(todoName)
.subscribe(todo => {
console.log('Todos组件中获取到的数据-->',todo)
})
}
}
这个,添加数据,就会打印出来数据
Todos组件中获取到的数据-->
Object
done: true
id: 7
name: "123"
__proto__:
在todo.component.ts
进行页面显示添加数据,this.todos.push(todo)
。
todo.component.ts
.....
export class TodoComponent implements OnInit {
....
addTodo(todoName: string) {
this.TodosService
.addTodo(todoName)
// (todo: Todo) 约束
.subscribe((todo: Todo)=> {
console.log('Todos组件中获取到的数据-->',todo)
this.todos.push(todo)
})
}
}
3. 切换任务状态
todos.service.ts
export class TodosService {
...
todosUrl = 'http://localhost:3000/todos';
// 切换任务
changeTodo(id: number,done: boolean) {
// const curTodo = this.todos.find(todo => todo.id === id)
// curTodo.done = !curTodo.done
return this.http.patch<Todo>(`${this.todosUrl}/${id}`,{ done })
}
...
}
todo.component.ts
.....
export class TodoComponent implements OnInit {
....
changeTodo(id: number) {
// this.TodosService.changeTodo(id)
const curTodo: Todo = this.todos.find(todo => todo.id === id)
this.TodosService
.changeTodo(id,!curTodo.done)
.subscribe((todo: Todo) => {
console.log('Todos组件中切换-->',todo)
curTodo.done = todo.done
})
}
}
4. 删除任务
todos.service.ts
export class TodosService {
...
todosUrl = 'http://localhost:3000/todos';
// 删除任务
delTodo(id: number) {
// const curIndex = this.todos.findIndex(todo => todo.id === id)
// this.todos.splice(curIndex,1)
return this.http.delete<Object>(`${this.todosUrl}/${id}`)
}
...
}
todo.component.ts
.....
export class TodoComponent implements OnInit {
....
delTodo(id: number) {
// this.TodosService.delTodo(id)
this.TodosService
.delTodo(id)
.subscribe(() => {
console.log('success')
this.todos = this.todos.filter(todo => todo.id !== id)
})
}
}
5. 总结
- 获取数据
- 在跟模块中导入
HttpClientModule
的模块(提供了http的能力)。做为主模块的依赖项才可以进行使用。 - 在服务中完成了增删改查的功能
- 在服务中导入HttpClient
- 注册到服务中
- 完成增删改查
- 在
getTodo
使用http方法,并将其返回
- 请求成功的话,在组件中的
subscribe
就会被执行,并且数据会做为参数传递。
- 在跟模块中导入
- 添加数据
- 在
addTodo
中使用http请求的post方法,第一个参数是接口地址,第二个参数是要添加的数据,将其返回。 - 当添加成功之后就会执行
subscribe
中的回调函数将数据添加到页面的todos数据中就可以。
- 在
- 切换/删除任务其实都是一样的,其两者都需要将ID传递给接口。
完
本文首次发布于 Azr的博客, 作者 @azrrrrr ,转载请保留原文链接.
原文链接: http://amor9.cn/2019/01/21/ng4/