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

前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到网站

简介

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

还有另一种方法可以使用所谓的 路由解析器,它允许您在导航到新路由之前获取数据。

可供使用的一个 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 生成的默认 Angular 项目构建。

npx @angular/cli new angular-route-resolvers-example --style=css --routing --skip-tests

这将配置一个新的 Angular 项目,其中样式设置为 “CSS”(而不是 “Sass”、“Less” 或 “Stylus”),启用了路由,并跳过了测试。

导航到新创建的项目目录:

cd angular-route-resolvers-example

此时,您已经有了一个带有 @angular/router 的新的 Angular 项目。

步骤 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 方法。该方法返回的任何内容都将是已解析的数据。

此代码将返回一个在 2 秒延迟后包装的字符串的可观察对象。

步骤 3 — 配置路由

为了体验两个不同的路由,您将需要两个新组件。home 将是登陆页面。top 将展示来自 Hacker News 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 — 在组件中访问已解析的数据

在组件中,您可以使用 ActivatedRoutesnapshot 对象的 data 属性来访问已解析的数据:


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

并在 Web 浏览器中访问 localhost:4200/top


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

现在,您可以使用 NewsService 替换 NewsResolver 中的字符串代码:

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 路由,将会看到一个代表 Hacker News 上热门帖子的 id 列表。

步骤 6 — 访问路由参数

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

以下是一个示例,您可以在解析器中使用 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'));
  }
}

接下来,向 NewsService 添加一个 getPost 方法:

// ...

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

并且在 app-routing.module.ts 中添加 PostResolverpost/:id 路由:

// ...

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,将会解析帖子 id 为 15392112 的数据。

步骤 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/httprxjs 实现的。

如果您想了解更多关于 Angular 的知识,请查看我们的 Angular 专题页面,了解更多练习和编程项目。

  • 56
    点赞
  • 31
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

白如意i

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值