Angularルーターでルートリゾルバーを使用する方法
序章
APIからのデータの取得と表示を処理する1つの方法は、ユーザーをコンポーネントにルーティングし、そのコンポーネントのngOnInit
フックで、サービスのメソッドを呼び出して必要なデータを取得することです。 データを取得している間、おそらくコンポーネントはロードインジケーターを表示できます。
route resolver
と呼ばれるものを使用する別の方法があります。これにより、新しいルートに移動する前にデータを取得できます。
使用可能なAPIの1つは、 Hacker NewsAPIです。 Hacker Newsは、リンクを共有して議論するためのWebサイトです。 APIを使用して、最も人気のある投稿を取得し、個々の投稿に関する情報を表示できます。
このチュートリアルでは、収集されたデータを表示するルートに移動する前に、HackerNewsAPIからデータを取得するルートリゾルバーを実装します。
前提条件
このチュートリアルを完了するには、次のものが必要です。
- Node.jsはローカルにインストールされます。これは、Node.jsのインストール方法とローカル開発環境の作成に従って実行できます。
- Angularプロジェクトのセットアップにある程度精通している。
このチュートリアルは、ノード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
これにより、スタイルが「CSS」(「Sass」、「Less」、「Stylus」ではなく)に設定され、ルーティングが有効になり、テストがスキップされる新しいAngularプロジェクトが構成されます。
新しく作成されたプロジェクトディレクトリに移動します。
cd angular-route-resolvers-example
この時点で、@angular/router
を使用した新しいAngularプロジェクトがあります。
ステップ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
メソッドが必要です。 そのメソッドから返されるものはすべて、解決されたデータになります。
このコードは、2秒の遅延後に文字列をラップするオブザーバブルを返します。
ステップ3—ルートの構成
2つの異なるルートを体験するには、2つの新しいコンポーネントが必要になります。 home
がランディングページになります。 そして、top
は、HackerNewsAPIからのトップ投稿を特集します。
まず、@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—コンポーネント内の解決されたデータへのアクセス
コンポーネントでは、ActivatedRoute
のsnapshot
オブジェクトのdata
プロパティを使用して、解決されたデータにアクセスできます。
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
そして、Webブラウザでlocalhost:4200/top
にアクセスします。
OutputThe message: Route!
top
ルートに移動すると、データが最初に解決されるため、2秒の遅延があることがわかります。
ステップ5—APIからのデータの解決
APIから実際にデータを取得して、より現実的なものにしましょう。 ここでは、HackerNewsAPIからデータを取得するサービスを作成します。
エンドポイントをリクエストするには、HttpClientが必要です。
まず、HttpClientModule
をapp.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); } }
これで、NewsResolver
の文字列コードをNewsService
に置き換えることができます。
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
ルートを見ると、HackerNewsのトップ投稿のid
を表す番号のリストが表示されます。
ステップ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`); } }
そして、PostResolver
とpost/: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/15392112
にアクセスすると、投稿ID15392112
のデータが解決されます。
ステップ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'); })); } }
または、EMPTY
observableを返し、ユーザーをルートパスに戻すこともできます。
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からデータを取得するときにエラーが発生した場合、これら2つのアプローチによりユーザーエクスペリエンスが向上します。
結論
このチュートリアルでは、収集されたデータを表示するルートに移動する前に、HackerNewsAPIからデータを取得するルートリゾルバーを実装しました。 これは、@angular/router
、@angular/common/http
、およびrxjs
を利用することによって実現されました。
Angularについて詳しく知りたい場合は、Angularトピックページで演習とプログラミングプロジェクトを確認してください。