简介
处理从API检索和显示数据的一种方法是将用户路由到一个组件,然后在该组件的ngOnInit
钩子中调用服务中的方法以获取必要的数据。在获取数据时,组件可能会显示加载指示器。
还有另一种方法可以使用所谓的路由解析器
,它允许您在导航到新路由之前获取数据。
一个可用的API是Hacker News API.黑客新闻是一个分享链接和讨论它们的网站。该API可用于检索最受欢迎的帖子,并显示有关单个帖子的信息。
在本教程中,您将实现一个路由解析器,该解析器在导航到显示收集的数据的路线之前从Hacker News API获取数据。
前提条件
要完成本教程,您需要:
- 本地安装node.js,可按照如何安装node.js并创建本地开发Environment.
- 对设置角度project.》有所了解
本教程通过了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验证。
第一步-设置项目
在本教程中,你将从一个默认的Angular项目中构建,该项目是用@angular/python
生成的。
1npx @angular/cli new angular-route-resolvers-example --style=css --routing --skip-tests
这将配置一个新的角度项目,将样式设置为),启用布线,并跳过测试。
导航到新创建的项目目录:
1cd angular-route-resolvers-example
至此,您就有了一个新的角度项目,其名称为@angular/router
。
第二步-构建解析器
让我们首先实现一个解析器,它在延迟2秒后返回一个字符串。这一小的概念验证有助于探索可应用于较大项目的布线的基本原理。
首先,在解析器自己的文件中为其创建一个单独的类:
1./node_modules/@angular/cli/bin/ng generate resolver news
这将使用@angular/cli
来生成一个名为News
的解析器:
1[label src/app/news.resolver.ts]
2import { Injectable } from '@angular/core';
3import { Resolve } from '@angular/router';
4
5import { Observable, of } from 'rxjs';
6import { delay } from 'rxjs/operators';
7
8@Injectable({
9 providedIn: 'root'
10})
11export class NewsResolver implements Resolve<Observable<string>> {
12 resolve(): Observable<string> {
13 return of('Route!').pipe(delay(2000));
14 }
15}
实现角度路由器的Resolve
接口需要该类有一个Resolve
方法。无论从该方法返回什么,都将是已解析的数据。
此代码将在延迟2秒后返回一个换行字符串的可观察对象。
第三步-配置路由
为了体验两条不同的路线,您需要两个新组件。主页
将成为网站的登陆页面。而top
将以黑客新闻API的热门帖子为特色。
首先,使用@angular/cli
生成一个home
组件:
1./node_modules/@angular/cli/bin/ng generate component home
然后,使用@angular/cli
生成一个top
组件:
1./node_modules/@angular/cli/bin/ng generate component top
现在,您可以设置路由模块以包括解析器。
1[label src/app/app-routing.module.ts]
2import { NgModule } from '@angular/core';
3import { Routes, RouterModule } from '@angular/router';
4
5import { NewsResolver } from './news.resolver';
6
7import { TopComponent } from './top/top.component';
8import { HomeComponent } from './home/home.component';
9
10const routes: Routes = [
11 {
12 path: '',
13 pathMatch: 'full',
14 component: HomeComponent
15 },
16 {
17 path: 'top',
18 component: TopComponent,
19 resolve: { message: NewsResolver }
20 }
21];
22
23@NgModule({
24 imports: [RouterModule.forRoot(routes)],
25 exports: [RouterModule]
26})
27export class AppRoutingModule { }
请注意解析器是如何像服务一样提供的,然后您将解析器包含在路由定义中。在这里,已解析的数据将在message
键下可用。
第四步-访问组件中的解析数据
在组件中,可以通过ActivatedRoute
‘S快照
对象的data
属性访问解析后的数据:
1[label src/app/top/top.component.ts]
2import { Component, OnInit } from '@angular/core';
3
4import { ActivatedRoute } from '@angular/router';
5
6@Component({ ... })
7export class TopComponent implements OnInit {
8 data: any;
9
10 constructor(private route: ActivatedRoute) {}
11
12 ngOnInit(): void {
13 this.data = this.route.snapshot.data;
14 }
15}
现在,在组件中,您可以访问`Route!像这样的消息:
1[label src/app/top/top.module.html]
2<p>The message: {{ data.message }}</p>
此时,您可以编译您的应用程序:
1npm start
并在Web浏览器中访问本地主机:4200/top
。
1[secondary_label Output]
2The message: Route!
在导航到top
路径时,您会发现现在延迟了2秒,因为数据是先解析的。
第五步-接口数据解析
让我们通过实际从API获取一些数据来使事情变得更加真实。在这里,您将创建一个从Hacker News API获取数据的服务。
您需要HttpClient来请求端点。
首先,将HttpClientModule
添加到app.mode.ts
中:
1[label src/app/app.module.ts]
2
3import { BrowserModule } from '@angular/platform-browser';
4import { NgModule } from '@angular/core';
5import { HttpClientModule } from '@angular/common/http';
6
7import { AppRoutingModule } from './app-routing.module';
8import { AppComponent } from './app.component';
9
10@NgModule({
11 declarations: [
12 AppComponent
13 ],
14 imports: [
15 BrowserModule,
16 HttpClientModule,
17 AppRoutingModule
18 ],
19 providers: [],
20 bootstrap: [AppComponent]
21})
22export class AppModule { }
然后,创建一个新服务:
1./node_modules/@angular/cli/bin/ng generate service news
这将使用@angular/cli
来生成一个名为News
的服务:
1[label src/app/news.service.ts]
2import { Injectable } from '@angular/core';
3import { HttpClient } from '@angular/common/http';
4
5@Injectable({
6 providedIn: 'root'
7})
8export class NewsService {
9 constructor(private http: HttpClient) { }
10
11 getTopPosts() {
12 const endpoint = 'https://hacker-news.firebaseio.com/v0/topstories.json';
13
14 return this.http.get(endpoint);
15 }
16}
现在您可以将NewsResolver
中的字符串code替换为NewsService
:
1[label src/app/news.resolver.ts]
2import { Injectable } from '@angular/core';
3import { Resolve } from '@angular/router';
4import { Observable } from 'rxjs';
5
6import { NewsService } from './news.service';
7
8export class NewsResolver implements Resolve<any> {
9 constructor(private newsService: NewsService) {}
10
11 resolve(): Observable<any> {
12 return this.newsService.getTopPosts();
13 }
14}
在这一点上,如果你在浏览器中查看排名靠前
的路线,你会看到一个代表黑客新闻
热门帖子ID
的数字列表。
Step 6-访问路由参数
您可以通过ActivatedRouteSnapshot
对象访问您的解析器中当前的路由参数。
下面是一个使用解析器访问当前路由的`id‘参数的示例。
首先,使用@angular/cli
生成一个名为post
的解析器:
1./node_modules/@angular/cli/bin/ng generate resolver news
然后修改post.Resolver.ts
,使用ActivatedRouteSnaphot
:
1[label src/app/post.resolver.ts]
2import { Injectable } from '@angular/core';
3import { Resolve, ActivatedRouteSnapshot } from '@angular/router';
4import { Observable } from 'rxjs';
5
6import { NewsService } from './news.service';
7
8@Injectable({
9 providedIn: 'root'
10})
11export class PostResolver implements Resolve<any> {
12 constructor(private newsService: NewsService) {}
13
14 resolve(route: ActivatedRouteSnapshot): Observable<any> {
15 return this.newsService.getPost(route.paramMap.get('id'));
16 }
17}
接下来,在NewsService
中添加一个getPost
方法:
1[label src/app/news.service.ts]
2// ...
3
4export class NewsService {
5 constructor(private http: HttpClient) { }
6
7 // ...
8
9 getPost(postId: string) {
10 const endpoint = 'https://hacker-news.firebaseio.com/v0/item';
11
12 return this.http.get(`${endpoint}/${postId}.json`);
13 }
14}
并将PostResolver
和post/:id
路由添加到app-routing.mode.ts
:
1[label src/app/app-routing.module.ts]
2// ...
3
4import { PostResolver } from './post.resolver';
5
6// ...
7
8const routes: Routes = [
9 // ...
10 {
11 path: 'post/:id',
12 component: PostComponent,
13 resolve: { newsData: PostResolver }
14 }
15];
16
17// ...
接下来,创建新的PostComponent
:
1./node_modules/@angular/cli/bin/ng generate component post
然后将post.Component.ts
修改为使用快照数据:
1[label src/app/post/post.component.ts]
2import { Component, OnInit } from '@angular/core';
3import { ActivatedRoute } from '@angular/router';
4
5@Component({ ... })
6export class PostComponent implements OnInit {
7 data: any;
8
9 constructor(private route: ActivatedRoute) { }
10
11 ngOnInit(): void {
12 this.data = this.route.snapshot.data;
13 }
14}
修改post.Component.html
,显示标题
:
1[label src/app/post/post.component.html]
2<p>{{ data.newsData.title }}</p>
现在,如果用户进入http://localhost:4200/post/15392112
,,则帖子ID为15392112
的数据将被解析。
Step 7-处理错误
如果在获取数据时出现错误,您可以使用RxJS的catch操作符]在解析器中捕获并处理错误。例如,类似于以下内容:
1[label src/app/news.resolver.ts]
2import { Injectable } from '@angular/core';
3import { Resolve } from '@angular/router';
4
5import { Observable, of } from 'rxjs';
6import { catchError } from 'rxjs/operators';
7
8import { NewsService } from './news.service';
9
10@Injectable()
11export class NewsResolver implements Resolve<any> {
12 constructor(private newsService: NewsService) {}
13
14 resolve(): Observable<any> {
15 return this.newsService.getTopPosts().pipe(catchError(() => {
16 return of('data not available at this time');
17 }));
18 }
19}
或者,您可以返回一个EMPTY
可观测对象,并将用户返回到根路径:
1[label src/app/news.resolver.ts]
2import { Injectable } from '@angular/core';
3import { Router, Resolve } from '@angular/router';
4
5import { Observable, EMPTY } from 'rxjs';
6import { catchError } from 'rxjs/operators';
7
8import { NewsService } from './news.service';
9
10@Injectable()
11export class NewsResolver implements Resolve<any> {
12 constructor(private router: Router, private newsService: NewsService) {}
13
14 resolve(): Observable<any> {
15 return this.newsService.getTopPosts().pipe(catchError(() => {
16 this.router.navigate(['/']);
17 return EMPTY;
18 }));
19 }
20}
如果从API检索数据时出现错误,这两种方法将带来更好的用户体验。
总结
在本教程中,您实现了一个路由解析器,该解析器在导航到显示所收集数据的路线之前从Hacker News API获取数据。这是通过使用@angular/router
、@angular/Common/http
和rxjs
来实现的。
如果您想了解更多有关ANGLE的信息,请查看我们的ANGLE主题页面以获取练习和编程项目。