在本教程中,我们将看到如何使用HttpClient服务进行API调用。 角度5. As of 角度5.0, the older Http service still works, but it’已弃用,并将在以后的版本中删除。这篇文章中的代码示例与Anguar 4.3、5.x及更高版本兼容。如果你的亲ject is still using Angular 4.2 or lower, including Angular 2
Http服务
Angular 4.3及更高版本中的HttpClient服务是Angular 2的Http服务和 $ http AngularJS 1.x提供的服务。它没有返回承诺,而是 http.get() 方法返回一个Observable对象。
后端API
-
GET / api / food
以JSON格式返回所有现有Food对象的数组。
-
GET / api / books
以JSON格式返回所有现有Book对象的数组。
-
GET / api / movies
以JSON格式返回所有现有Movies对象的数组。
-
POST / api / food
在后端数据存储区中创建一个新的Food对象。在请求正文中接受JSON对象。
如果成功返回一个 200 好 响应,其中包含表示存储在服务器上的数据的JSON对象,包括自动编号的ID。 -
放置/ api / food / {food_id}
更新现有的Food对象。在请求正文中接受JSON对象。
如果成功,则返回一个 200 好 响应,其中包含表示存储在服务器上的数据的JSON对象。 -
删除/ api / food / {food_id}
删除现有的食物对象。不需要响应主体。
如果成功,则返回一个 200 好 响应,其中包含一个表示Food对象的JSON对象,该对象在删除之前就已经存在。
整个Express API代码如下。
const express = require('express');
const bodyParser = require('body-parser');
const path = require('path');
const app = express();
app.use(express.static(__dirname));
app.use(bodyParser.json()); // support json encoded bodies
// some data for the API
var foods = [
{ "id": 1, "name": "Donuts" },
{ "id": 2, "name": "Pizza" },
{ "id": 3, "name": "Tacos" }
];
var books = [
{ "title": "Hitchhiker's Guide to the Galaxy" },
{ "title": "的 Fellowship of the Ring" },
{ "title": "Moby Dick" }
];
var movies = [
{ "title": "Ghostbusters" },
{ "title": "Star Wars" },
{ "title": "Batman Begins" }
];
// the "index" route, which serves the Angular app
app.get('/', function (req, res) {
res.sendFile(path.join(__dirname,'/dist/index.html'))
});
// the GET "books" API endpoint
app.get('/api/books', function (req, res) {
res.send(books);
});
// the GET "movies" API endpoint
app.get('/api/movies', function (req, res) {
res.send(movies);
});
// the GET "foods" API endpoint
app.get('/api/food', function (req, res) {
res.send(foods);
});
// POST endpoint for creating a new food
app.post('/api/food', function (req, res) {
// calculate the next ID
let id = 1;
if (foods.length > 0) {
let maximum = Math.max.apply(Math, foods.map(function (f) { return f.id; }));
id = maximum + 1;
}
let new_food = {"id": id, "name": req.body.name};
foods.push(new_food);
res.send(new_food);
});
// PUT endpoint for editing food
app.put('/api/food/:id', function (req, res) {
let id = req.params.id;
let f = foods.find(x => x.id == id);
f.name = req.body.name;
res.send(f);
});
// DELETE endpoint for deleting food
app.delete('/api/food/:id', function (req, res) {
let id = req.params.id;
let f = foods.find(x => x.id == id);
foods = foods.filter(x => x.id != id);
res.send(f);
});
// HTTP listener
app.listen(3000, function () {
console.log('Example listening on port 3000!');
});
module.exports = app;
HttpClient入门
使用角度 HttpClient,我们需要将其注入应用程序的依赖项中:
import { NgModule, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { HttpClientModule } from '@angular/common/http'; // replaces previous Http service
import { FormsModule } from '@angular/forms';
import { DemoService } from './demo.service'; // our custom service, see below
import { AppComponent } from './app.component';
@NgModule({
imports: [BrowserModule, FormsModule, HttpClientModule],
declarations: [AppComponent],
providers: [DemoService],
schemas: [CUSTOM_ELEMENTS_SCHEMA],
bootstrap: [AppComponent]
})
export class AppModule { }
构建角度组件
我们的演示应用程序仅包含一个简单的组件,其中包含一些元素以显示一些简单的数据。我们将从几个JSON文件加载数据,以模拟API调用。
import {Component} from '@angular/core';
import {DemoService} from './demo.service';
import {Observable} from 'rxjs/Rx';
@Component({
selector: 'demo-app',
template:`
<h1>Angular 5 HttpClient Demo App</h1>
<h2>Foods</h2>
<ul>
<li *ngFor="let food of foods">{{food.name}}</li>
</ul>
`
})
export class AppComponent {
public foods;
constructor(private _demoService: DemoService) { }
}
执行单个HTTP请求
我们可以使用HttpClient来请求单个资源 http.get。这与Angular 2 Http服务非常相似。注意,我们不再需要.map((res:Response)=> res.json()`因为HttpClient为我们处理了这个
import {Injectable} from '@angular/core';
import { HttpClient, HttpHeaders } from '@angular/common/http';
import {Observable} from 'rxjs/Observable';
const httpOptions = {
headers: new HttpHeaders({ 'Content-Type': 'application/json' })
};
@Injectable()
export class DemoService {
constructor(private http:HttpClient) {}
// Uses http.get() to load data from a single API endpoint
getFoods() {
return this.http.get('/api/food');
}
}
我们的演示服务发出HTTP请求并返回Observable对象。要从服务中实际获取数据,我们需要更新组件以订阅Observable
...
ngOnInit() {
this.getFoods();
}
//+ getFoods() {
//+ this._demoService.getFoods().subscribe(
//+ data => { this.foods = data},
//+ err => console.error(err),
//+ () => console.log('done loading foods')
//+ );
//+ }
的 订阅() 方法采用三个参数作为事件处理程序。它们分别称为onNext,onError和onCompleted。 onNext方法将接收HTTP响应数据。 Observable支持数据流,并且可以多次调用此事件处理程序。但是,对于HTTP请求,Observable通常将在一次调用中发出整个数据集。如果HTTP请求返回错误代码(例如404),则将调用onError事件处理函数。onCompleted事件处理函数在Observable完成返回所有数据之后执行。在 Http.get() 调用,因为我们需要的所有数据都传递给onNext处理程序。
执行多个并发HTTP请求
很多时候,我们需要从多个源中加载数据,并且需要延迟后加载逻辑,直到所有数据都加载完毕。 ReactiveX Observables提供了一种称为 forkJoin() 包装多个Observable。它的 订阅() 方法在整个Observable集合上设置处理程序。
要运行并发的HTTP请求,请在我们的服务中添加以下代码
...
@Injectable()
export class DemoService {
constructor(private http:HttpClient) {}
// Uses http.get() to load data from a single API endpoint
getFoods() {
return this.http.get('/api/food');
}
//+ Uses Observable.forkJoin() to run multiple concurrent http.get() requests.
//+ // 的 entire operation will result in an error state if any single request fails.
//+ getBooksAndMovies() {
//+ return Observable.forkJoin(
//+ this.http.get('/api/books'),
//+ this.http.get('/api/movies')
//+ );
//+ }
}
使用时 Http.get() 和 Observable.forkJoin() 一起,onNext处理程序将仅执行一次,并且仅在所有HTTP请求成功完成之后才执行。它将接收一个包含所有请求的组合响应数据的数组。在这种情况下,我们的图书数据将存储在 数据[0] 我们的电影数据将存储在 数据[1].
如果任一HTTP请求返回错误代码,则onError处理程序将在此处运行
接下来,我们在组件中订阅新方法:
import {Component} from '@angular/core';
import {DemoService} from './demo.service';
import {Observable} from 'rxjs/Rx';
@Component({
selector: 'demo-app',
template:`
<h1>Angular 5 HttpClient Demo App</h1>
<p>This is a complete mini-CRUD application using an Express back-end. See src / app / demo.service.ts for the API call code.</p>
<h2>Foods</h2>
<ul>
<li *ngFor="let food of foods">{{food.name}}</li>
</ul>
//+ <h2>Books 和 Movies</h2>
//+ <p>This is an example of loading data from multiple endpoints using Observable.forkJoin(). 的 API calls here are read-only.</p>
//+ <h3>Books</h3>
//+ <ul>
//+ <li *ngFor="let book of books">{{book.title}}</li>
//+ </ul>
//+ <h3>Movies</h3>
//+ <ul>
//+ <li *ngFor="let movie of movies">{{movie.title}}</li>
//+ </ul>
`
})
export class AppComponent {
public foods;
//+ public books;
//+ public movies;
...
getFoods() {
...
}
//+ getBooksAndMovies() {
//+ this._demoService.getBooksAndMovies().subscribe(
//+ data => {
//+ this.books = 数据[0]
//+ this.movies = 数据[1]
//+ }
//+ // No error or completion callbacks here. 的y are optional, but
//+ // you will get console errors if the Observable is in an error state.
//+ );
//+ }
}
将数据写入API
要将数据写入我们的API,我们需要向DemoService类添加几个新方法:
...
@Injectable()
export class DemoService {
constructor(private http:HttpClient) {}
...
//+ createFood(food) {
//+ let body = JSON.stringify(food);
//+ return this.http.post('/api/food/', body, httpOptions);
//+ }
//+ updateFood(food) {
//+ let body = JSON.stringify(food);
//+ return this.http.put('/api/food/' + food.id, body, httpOptions);
//+ }
//+ deleteFood(food) {
//+ return this.http.delete('/api/food/' + food.id);
//+ }
}
Firefox中可能的错误
在撰写本文时,我的一个项目的API调用在Firefox中运行时失败。看来Angular 2并未发送Content-type:application / json标头和请求。如果您的API支持此功能,则可以通过更改API URL以包括.json扩展名来解决此问题。 (例如,/ api / food / 1.json).
从我们的组件创建和保存数据
现在我们已经有了服务,我们可以向AppComponent添加一些基本的CRUD功能。
import {Component} from '@angular/core';
import {DemoService} from './demo.service';
import {Observable} from 'rxjs/Rx';
@Component({
selector: 'demo-app',
template:`
<h1>Angular 5 HttpClient Demo App</h1>
<p>This is a complete mini-CRUD application using an Express back-end. See src / app / demo.service.ts for the API call code.</p>
<h2>Foods</h2>
<ul>
//- <li *ngFor="let food of foods">{{food.name}}</li>
//+ <li *ngFor="let food of foods"><input type="text" name="food-name" [(ngModel)]="food.name">
//+ <button (click)="updateFood(food)">Save</button>
//+ <button (click)="deleteFood(food)">Delete</button>
//+ </li>
</ul>
//+ <p>Create a new food: <input type="text" name="food_name" [(ngModel)]="food_name"><button (click)="createFood(food_name)">Save</button></p>
<h2>Books 和 Movies</h2>
...
`
})
export class AppComponent {
public foods;
public books;
public movies;
+ public food_name;
...
getFoods() {
...
}
getBooksAndMovies() {
...
}
//+ createFood(name) {
//+ let food = {name: name};
//+ this._demoService.createFood(food).subscribe(
//+ data => {
//+ // refresh the list
//+ this.getFoods();
//+ return true;
//+ },
//+ error => {
//+ console.error("Error saving food!");
//+ return Observable.throw(error);
//+ }
//+ );
//+ }
//+ updateFood(food) {
//+ this._demoService.updateFood(food).subscribe(
//+ data => {
//+ // refresh the list
//+ this.getFoods();
//+ return true;
//+ },
//o+ error => {
//+ console.error("Error saving food!");
//+ return Observable.throw(error);
//+ }
//+ );
//+ }
//+ deleteFood(food) {
//+ if (confirm("Are you sure you want to delete " + food.name + "?")) {
//+ this._demoService.deleteFood(food).subscribe(
//+ data => {
//+ // refresh the list
//+ this.getFoods();
//+ return true;
//+ },
//+ error => {
//+ console.error("Error deleting food!");
//+ return Observable.throw(error);
//+ }
//+ );
//+ }
//+ }
}
您会注意到,我们在模板中添加了一些基本表单字段和按钮,以及新方法 createFood(), updateFood()和 deleteFood() 到组件类。当用户单击模板中的按钮并处理保存和删除数据时,将调用它们。
为简单起见,我使用了一个简单的JavaScript 确认() 对话框作为删除确认。一个增强功能可能是使用另一个Angular组件实现外观更好的对话框。
获取更多Angular 2+文章 这里!