介绍
处理从 API 检索和显示数据的一种方法是将用户路由到组件,然后在该组件的ngOnInit
挂钩中调用服务中的方法以获取必要的数据。在获取数据时,也许组件可以显示加载指示器。
还有另一种使用所谓的 a 的方法route resolver
,它允许您在导航到新路线之前获取数据。
一种可用的 API 是Hacker News API。Hacker News 是一个分享链接和讨论链接的网站。该 API 可用于检索最受欢迎的帖子并显示有关单个帖子的信息。
在本教程中,您将实现一个路由解析器,在导航到显示收集到的数据的路由之前从 Hacker News API 获取数据。
先决条件
要完成本教程,您需要:
- Node.js 安装在本地,您可以按照如何安装 Node.js 和创建本地开发环境来完成。
- 对设置 Angular 项目有一定的了解。
本教程已通过 Node v15.3.0、npm
v6.14.9、@angular/core
v11.0.1、@angular/common
v11.0.1、@angular/router
v11.0.1 和rxjs
v6.6.0 验证。
步骤 1 — 设置项目
出于本教程的目的,您将从使用@angular/cli
.
- npx @angular/cli new angular-route-resolvers-example --style=css --routing --skip-tests
这将配置一个新的 Angular 项目,其样式设置为“CSS”(与“Sass”、Less 或“Stylus”相反)、启用路由并跳过测试。
导航到新创建的项目目录:
- cd angular-route-resolvers-example
此时,您有一个新的 Angular 项目@angular/router
。
第 2 步 – 构建解析器
让我们从实现一个在 2 秒延迟后返回字符串的解析器开始。这个小的概念证明可以帮助探索可应用于更大项目的布线的基本原理。
首先,在自己的文件中为解析器创建一个单独的类:
- ./node_modules/@angular/cli/bin/ng generate resolver news
这将使用@angular/cli
生成一个名为 的解析器news
:
import { Injectable } from '@angular/core';
import { Resolve } from '@angular/router';
import { Observable, of } from 'rxjs';
import { delay } from 'rxjs/operators';
@Injectable({
providedIn: 'root'
})
export class NewsResolver implements Resolve<Observable<string>> {
resolve(): Observable<string> {
return of('Route!').pipe(delay(2000));
}
}
实现 Angular 路由器的Resolve
接口需要类有一个resolve
方法。从该方法返回的任何内容都将是已解析的数据。
此代码将返回一个 observable,它在 2 秒的延迟后包装一个字符串。
第 3 步 – 配置路由
为了体验两条不同的路线,您将需要两个新组件。home
将是登陆页面。并且top
将有来自黑客新闻API顶帖。
首先,使用@angular/cli
生成一个home
组件:
- ./node_modules/@angular/cli/bin/ng generate component home
然后,使用@angular/cli
生成一个top
组件:
- ./node_modules/@angular/cli/bin/ng generate component top
现在您可以设置路由模块以包含解析器。
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { NewsResolver } from './news.resolver';
import { TopComponent } from './top/top.component';
import { HomeComponent } from './home/home.component';
const routes: Routes = [
{
path: '',
pathMatch: 'full',
component: HomeComponent
},
{
path: 'top',
component: TopComponent,
resolve: { message: NewsResolver }
}
];
@NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule]
})
export class AppRoutingModule { }
注意解析器是如何像服务一样提供的,然后将解析器包含在路由定义中。在这里,已解析的数据将在message
密钥下可用。
第 4 步 – 访问组件中的解析数据
在组件中,您可以使用‘s对象的data
属性访问解析的数据:ActivatedRoute
snapshot
import { Component, OnInit } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
@Component({ ... })
export class TopComponent implements OnInit {
data: any;
constructor(private route: ActivatedRoute) {}
ngOnInit(): void {
this.data = this.route.snapshot.data;
}
}
现在,在组件中,您可以Route!
像这样访问消息:
<p>The message: {{ data.message }}</p>
此时,您可以编译您的应用程序:
- npm start
并localhost:4200/top
在网络浏览器中访问。
OutputThe message: Route!
当导航到top
路线时,您会发现现在有 2 秒的延迟,因为首先解析数据。
第 5 步 – 从 API 解析数据
让我们通过从 API 实际获取一些数据来让事情变得更加真实。在这里,您将创建一个从 Hacker News API 获取数据的服务。
您将需要HttpClient来请求端点。
首先,添加HttpClientModule
到app.module.ts
:
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { HttpClientModule } from '@angular/common/http';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
@NgModule({
declarations: [
AppComponent
],
imports: [
BrowserModule,
HttpClientModule,
AppRoutingModule
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
然后,创建一个新服务:
- ./node_modules/@angular/cli/bin/ng generate service news
这将使用@angular/cli
生成名为 的服务news
:
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
@Injectable({
providedIn: 'root'
})
export class NewsService {
constructor(private http: HttpClient) { }
getTopPosts() {
const endpoint = 'https://hacker-news.firebaseio.com/v0/topstories.json';
return this.http.get(endpoint);
}
}
现在你可以替换字符串代码NewsResolver
有NewsService
:
import { Injectable } from '@angular/core';
import { Resolve } from '@angular/router';
import { Observable } from 'rxjs';
import { NewsService } from './news.service';
export class NewsResolver implements Resolve<any> {
constructor(private newsService: NewsService) {}
resolve(): Observable<any> {
return this.newsService.getTopPosts();
}
}
此时,如果您top
在浏览器中查看路线,您将看到一个数字列表,代表id
Hacker News 上的热门帖子。
第 6 步 – 访问路由参数
您可以使用ActivatedRouteSnapshot
对象访问解析器中的当前路由参数。
这是一个示例,您将使用解析器来访问id
当前路由的参数。
首先,使用@angular/cli
生成一个名为 的解析器post
:
- ./node_modules/@angular/cli/bin/ng generate resolver news
然后,修改post.resolver.ts
使用ActivatedRouteSnapshot
:
import { Injectable } from '@angular/core';
import { Resolve, ActivatedRouteSnapshot } from '@angular/router';
import { Observable } from 'rxjs';
import { NewsService } from './news.service';
@Injectable({
providedIn: 'root'
})
export class PostResolver implements Resolve<any> {
constructor(private newsService: NewsService) {}
resolve(route: ActivatedRouteSnapshot): Observable<any> {
return this.newsService.getPost(route.paramMap.get('id'));
}
}
接下来,添加一个getPost
方法到NewsService
:
// ...
export class NewsService {
constructor(private http: HttpClient) { }
// ...
getPost(postId: string) {
const endpoint = 'https://hacker-news.firebaseio.com/v0/item';
return this.http.get(`${endpoint}/${postId}.json`);
}
}
并添加PostResolver
和post/:id
路由到app-routing.module.ts
:
// ...
import { PostResolver } from './post.resolver';
// ...
const routes: Routes = [
// ...
{
path: 'post/:id',
component: PostComponent,
resolve: { newsData: PostResolver }
}
];
// ...
接下来,创建新的PostComponent
:
- ./node_modules/@angular/cli/bin/ng generate component post
然后,修改post.component.ts
为使用快照数据:
import { Component, OnInit } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
@Component({ ... })
export class PostComponent implements OnInit {
data: any;
constructor(private route: ActivatedRoute) { }
ngOnInit(): void {
this.data = this.route.snapshot.data;
}
}
并修改post.component.html
以显示title
:
<p>{{ data.newsData.title }}</p>
现在,如果用户转到http://localhost:4200/post/15392112
,15392112
则将解析帖子 ID 的数据。
步骤 7 — 处理错误
如果在获取数据时出现错误,您可以使用RxJS 的 catch运算符在解析器中捕获并处理错误。例如这样的事情:
import { Injectable } from '@angular/core';
import { Resolve } from '@angular/router';
import { Observable, of } from 'rxjs';
import { catchError } from 'rxjs/operators';
import { NewsService } from './news.service';
@Injectable()
export class NewsResolver implements Resolve<any> {
constructor(private newsService: NewsService) {}
resolve(): Observable<any> {
return this.newsService.getTopPosts().pipe(catchError(() => {
return of('data not available at this time');
}));
}
}
或者你可以返回一个EMPTY
observable 并将用户返回到根路径:
import { Injectable } from '@angular/core';
import { Router, Resolve } from '@angular/router';
import { Observable, EMPTY } from 'rxjs';
import { catchError } from 'rxjs/operators';
import { NewsService } from './news.service';
@Injectable()
export class NewsResolver implements Resolve<any> {
constructor(private router: Router, private newsService: NewsService) {}
resolve(): Observable<any> {
return this.newsService.getTopPosts().pipe(catchError(() => {
this.router.navigate(['/']);
return EMPTY;
}));
}
}
如果从 API 检索数据时出现错误,这两种方法将带来更好的用户体验。
结论
在本教程中,您实现了一个路由解析器,它在导航到显示收集到的数据的路由之前从 Hacker News API 获取数据。这是利用实现@angular/router
,@angular/common/http
和rxjs
。
如果您想了解有关 Angular 的更多信息,请查看我们的 Angular 主题页面以获取练习和编程项目。