如何在 Angular Router 中使用路由解析器

介绍

处理从 API 检索和显示数据的一种方法是将用户路由到组件,然后在该组件的ngOnInit挂钩中调用服务中的方法以获取必要的数据。在获取数据时,也许组件可以显示加载指示器。

还有另一种使用所谓的 a 的方法route resolver,它允许您在导航到新路线之前获取数据。

一种可用的 API 是Hacker News APIHacker News 是一个分享链接和讨论链接的网站。该 API 可用于检索最受欢迎的帖子并显示有关单个帖子的信息。

在本教程中,您将实现一个路由解析器,在导航到显示收集到的数据的路由之前从 Hacker News API 获取数据。

先决条件

要完成本教程,您需要:

本教程已通过 Node v15.3.0、npmv6.14.9、@angular/corev11.0.1、@angular/commonv11.0.1、@angular/routerv11.0.1 和rxjsv6.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

src/app/news.resolver.ts
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

现在您可以设置路由模块以包含解析器。

src/app/app-routing.module.ts
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属性访问解析的数据ActivatedRoutesnapshot

src/app/top/top.component.ts
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!像这样访问消息:

src/app/top/top.module.html
<p>The message: {{ data.message }}</p>

此时,您可以编译您的应用程序:

  • npm start

localhost:4200/top在网络浏览器中访问

Output
The message: Route!

当导航到top路线时,您会发现现在有 2 秒的延迟,因为首先解析数据。

第 5 步 – 从 API 解析数据

让我们通过从 API 实际获取一些数据来让事情变得更加真实。在这里,您将创建一个从 Hacker News API 获取数据的服务。

您将需要HttpClient来请求端点。

首先,添加HttpClientModuleapp.module.ts

src/app/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

src/app/news.service.ts
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);
  }
}

现在你可以替换字符串代码NewsResolverNewsService

src/app/news.resolver.ts
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在浏览器中查看路线,您将看到一个数字列表,代表idHacker News 上的热门帖子。

第 6 步 – 访问路由参数

您可以使用ActivatedRouteSnapshot对象访问解析器中的当前路由参数

这是一个示例,您将使用解析器来访问id当前路由参数。

首先,使用@angular/cli生成一个名为 的解析器post

  • ./node_modules/@angular/cli/bin/ng generate resolver news

然后,修改post.resolver.ts使用ActivatedRouteSnapshot

src/app/post.resolver.ts
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

src/app/news.service.ts
// ...

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`);
  }
}

并添加PostResolverpost/:id路由到app-routing.module.ts

src/app/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为使用快照数据:

src/app/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

src/app/post/post.component.html
<p>{{ data.newsData.title }}</p>

现在,如果用户转到http://localhost:4200/post/1539211215392112则将解析帖子 ID 的数据

步骤 7 — 处理错误

如果在获取数据时出现错误,您可以使用RxJS 的 catch运算符在解析器中捕获并处理错误例如这样的事情:

src/app/news.resolver.ts
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');
    }));
  }
}

或者你可以返回一个EMPTYobservable 并将用户返回到根路径:

src/app/news.resolver.ts
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/httprxjs

如果您想了解有关 Angular 的更多信息,请查看我们的 Angular 主题页面以获取练习和编程项目。

觉得文章有用?

点个广告表达一下你的爱意吧 !😁