2018/07/13に更新されました。 新しい規約を使用するために、コードを大幅にオーバーホールしました。 AngularとRxJSを利用可能な最新バージョンにアップグレードし、サンプルをStackBlitzに移動しました。
この投稿の目的は、Stackblitz上のサンプルコードを提供するとともに、Angularルートリゾートがどのように動作するかを10分で実証することです。 AngularJS からのルートリゾルブにすでに慣れている場合は、イントロをスキップして、サンプルアプリのセクションに直接ジャンプすることをお勧めします。
目次
イントロ
サンプルアプリ
- ステップ1:リゾルバクラスを作成
- ステップ2:ルートにリゾルブガードを追加
- ステップ3:リゾルブガードを追加。 コンポーネントのアクティブなルートから解決されたデータを取得する
Notes & Tips
- 重複した API 呼び出しを避ける
- Multiple Resolves on the Component 同じルート
- Route Params をリゾルバに渡す
Closing Notes
Intro
Route resolves は、事前にリゾールする方法以外の何物でもありません。コンポーネントが初期化される前に、必要なデータを取得します。 通常、このデータはAPIから取得されます。 例えば、その月の日次売上をチャートで表示することだけが役割のコンポーネントがあるとします。売上データが利用可能になる前に、ビューをレンダリングしたり、このコンポーネントをロードする意味はありません。 実際、多くのチャートライブラリは、データを供給する前にチャートを初期化しようとするとエラーを投げます (*ngFor
もそうです)。 もちろん、*ngIf
でhtmlを隠したり、必要なデータが読み込まれるまで一時的に空の配列を供給することで、この問題を簡単に回避することができます。 しかし、resolve なしでも何とかなりますが、resolve を実装することで、次のようにコードをより読みやすく、保守しやすくすることができます:
- コンポーネントのテンプレートとコードにおける混乱をなくす。
- どのデータがプリフェッチされなければならないかを示すことにより、意図を明確にする。
これらの利点にもかかわらず、多くのサイトはコンポーネントの大部分を表示し、データがまだロードされているセクションでスピナーを表示することを支持して、リゾルブの使用を避けています。 いくつかのケースでは、このアプローチは UX の観点から望ましいものであり、resolve は使用されるべきではありません。 あなたの判断で行ってください。
Sample App
Open Sample App
ご覧のように、出発点は、「ホーム」と「ニュース」という 2 つのルートを持つ非常に基本的なアプリです。 対応するタブをクリックすることにより、ルート間を移動します。 News コンポーネントがロードされる前に API からニュースをプリフェッチするための解決策を追加する予定です。
Step 1: 必要なデータを事前にフェッチするために Http 呼び出しを行うリゾルバ クラスを作成します。
app
フォルダー内に新しい Typescript ファイルを作成し、名前を付けます。
news-resolver.service.ts
次に、次のコードをコピーして新しいファイルに貼り付けます (以下で説明):
上記のコードで行ったことを分解します。
TypeScript NewsResolver
クラスを新規作成。
クラスに Resolve
インターフェイスを追加。これはオプションだが、リゾルバとして使用予定のクラスは resolve メソッドを実装しなければいけないので、これはよい規則といえる。
NewsResolver
に resolve()
メソッドを追加 – これは私たちが必要とするデータを返すためのメソッドである。 resolve ガードのデータを返すメソッドに “resolve” という名前を付けることは任意ではありません – このメソッドが他の名前で呼ばれていた場合、リゾルバは動作しません。
上記のコードで GET
ではなく POST
要求が誤って使われていることに気付いた場合、それは全く正しいです; 実際のアプリで、これは GET
要求となるはずです。 ここでの例では、テスト API エンドポイントを提供するサイトである を利用しています。
次に進む前に、先ほど作成したリゾルバ クラスをルーティング モジュールに組み込む必要があります。 app-routing.module.ts
に移動して、NewsResolver
を providers
配列に追加します。 または、Angular 2 を使い始めたばかりであれば、app-routing.module.ts
の内容を以下のコードに置き換えるだけです – 変更点には注記があります:
これで、リゾルバクラスを定義できました。 次のステップでは、これをルートに追加します。
Code After Adding the Resolver Class
Step 2: Add a Resolve Guard to the route.
In app-routing.module.ts
, change the following line of code { path: 'news', component: NewsComponent }
to:
{ path: 'news', component: NewsComponent, resolve: { news: NewsResolver }}
ここでやったことは、news
ルートに先ほど定義した resolve ガードを追加するだけです。 これは、 の resolve()
メソッドがデータを返すのを待ってから NewsComponent
を表示しなければならないことを Angular に伝えます。
重要な点は、コードの news: NewsResolver
行の news
は、リゾルバーが返すどのデータにも私が選んだ名前を付けている点です。
少し余談ですが、一般的に Angular ルート ガードをよく知らず、もっと知りたい場合は、ドキュメントをここにご覧ください。 ガードについて詳細に説明することはこの記事の範囲外ですが、resolve
以外にも利用できるガードがあることを知っておく必要があります。
Resolve ガードを追加したコード
Step 3: コンポーネントのアクティブなルートから解決済みのデータを取得する。
resolver クラスを持っているので、それをルートに追加し、データをプリフェッチすることができる。 最後のステップは、app/news.component.ts
にある NewsComponent
でプリフェッチされたデータにアクセスすることです。 そのファイルに移動し、次の ES6 モジュールを追加します。
import { ActivatedRoute } from '@angular/router';
次に、NewsComponent
クラス定義の先頭に次のコードを追加して、News コンポーネントのコンストラクターで ActivatedRoute
を提供します。 ここで気になるのは、resolve ガードから ActivatedRoute
に読み込まれるニュース データです。 ルートから解決されたニュース データを取得するには、ニュース データを保持するプロパティを追加します:
public news: any;
そして、NewsComponent
が初期化されるときにアクティブなルートからデータを取得します:
ngOnInit(): void { this.news = this.route.snapshot.data; }
これは事実上それです。 現在のコンテンツ:
<div>This is just a placeholder for now. News will go here. </div>
を削除し、
ニュース タブをクリックすると、最新のニュース「The sky is blue」が表示されるように、ルートから解決したニュースを表示するニュース コンポーネントのテンプレートを変更する必要があります。
完成したコード
Notes & Tips
– Resolve は冗長な API 呼び出しを引き起こす可能性があります。 解決は、コンポーネントがロードされるたびにデータを取得します。 これはしばしば、パフォーマンスに悪影響を及ぼす不要な API 呼び出しにつながります。 resolveが頻繁に変更されないデータを取得する場合、 resolveが返すデータをresolverクラスのプロパティに書き、 それが既に設定されている場合は単にそのプロパティを返すことを検討してください。 例えば、この例では、最初は未定義の news
プロパティを以下のように追加します。 public news: any = undefined;
のようなプロパティをニュースリゾルバに追加する。 次に、resolve()
メソッドで、news
プロパティがすでに設定されているかどうかを確認し、設定されている場合は API を呼び出さずにその値を返します(例:
resolve(): Observable<any> { if (this.news) { return this.getSavedNews(); } else { return this.getNewsFromApi() } }
完全なコード例は以下のとおりです。 observable の構文が少し変わっているように見えるのは、私が RxJS 6
を使っているからです。 もし復習が必要なら、最近 RxJS
に導入された変更に関する良いチュートリアルがここにあります。
Return Saved Data, if Already Fetched from API
Naturally, you could go further and set a time period which the data is valid by not only saving the data, but adding another timestamp property and making a API call if the data is older than x.
– AngularJS のように、同じルートで複数の resolve を使用することが可能です。 resolveの呼び出しは並行して行われ、すべての呼び出しがデータを返した後にのみコンポーネントがロードされます。 複数のリゾルブを使用するには、それらをルートに追加するだけです。
それから、追加のリゾルブ データは、単一のリゾルブと同様に、ルート スナップショットからアクセスできます。 例えば、ニュース記事のタイトルのリストを表示するコンポーネントがあるとします。 ストーリーがクリックされると、記事全体を表示する別のコンポーネントが開かれます。 そのコンポーネントを読み込む前に、クリックされたタイトルのニュース記事の内容を事前に取得する必要があります。これは resolve メソッドで行うことができます。 resolver クラスは ActivatedRoute
にアクセスできるので、クリックされた記事の ID を取得することができます。 例として、以下のリンクにある新しい src/news-story-resolver.service.ts
ファイルを確認してください。 新しいコンポーネントへのリンクは、[News] タブ (news.component.ts
) に追加されました。
Code With Parameterized Resolve
(パラメーターを指定して解決するコード