Part7: ルーティング

穂乃果「わーい」
ことり「すごーい」
海未「たーのしー」


海未「私としたことが・・・語彙を失うとはこういうことなのですね」
穂乃果「でもでもっ!ほんとにすごかったよねっ!」
ことり「ちょっと早い桜が咲いたね♪・・・横浜アリーナに」
海未「技術はまだまだ拙いですが、チームワークと精神力、パフォーマーシップにはすでに十分な貫禄を感じます」
穂乃果「羽根、ちゃんと受け取ってくれたんだねっ!」
ことり「2日目だけだけど、現地チケット手に入ってよかったよね♪」
海未「ええ、本当に。譲っていただいた方、ありがとうございました」

穂乃果「ねえねえ、あれ、やってみようよ!」
ことり「あれ?・・・うん、いいと思う!」
海未「・・・仕方ないですね(実はやってみたかったとは言えない)」

穂乃果「0から1へ! 今、全力で輝こう! あくあーーーっ!」
ほのことうみ「「「さーーーんしゃいん!!!」」」

(2017/3/3執筆)


海未「さて今回は、ルーティング、要するに画面遷移を扱います。完成形はPart2: できあがりイメージを確認しておいてくださいね」
穂乃果「ここまで1画面だけでやってきたもんね」
海未「今回はボリュームが大きいので、最初に作業全体の概略を整理しましょう。まず、AppComponentは遷移を司るだけにして、そこにあった処理は他のコンポーネントに切り出します」
ことり「いろいろ詰め込んじゃってたもんね」
海未「それから、Augularのルーターの仕組みを使って画面遷移できるようにします。最後に、ダッシュボードのコンポーネントを作成してルーティングに組み込んで完成です」


海未「まずはアイドルの一覧を表示している部分を別コンポーネントにしましょう。といっても処理の大部分を移すことになるので、app.component.tsの名前を変えてから、新規にapp.component.tsを作ることにしましょう」
ことり「app.component.tsが新しいアイドル一覧コンポーネントになるんだね」
海未「そうです。app.component.tsidols.component.tsにリネームしてください。それから、AppComponentクラスをIdolsComponentに、セレクタmy-appmy-idolsに、それぞれ修正してください」
穂乃果「いつもみたいにapp.module.tsにも書き足せばいいのかな?」
海未「さすがに穂乃果も学習してきましたね。IdolsComponentをインポートして、declarationsに追加してください」

海未「次にapp.component.tsを作成します。最初はシンプルに、このような内容にしましょう」

海未「app.module.tsはこのようにします。この際、IdolsComponentprovidersにあったIdolServiceをこのAppModuleに移します。」

ことり「IdolsComponentprovidersのところは消しちゃっても大丈夫?」
海未「AppModuleに書かれていれば全てのコンポーネントでIdolServiceが使えますから、消してしまって大丈夫です」


海未「では本題のルーティングの話に進みましょう。下準備として、index.html<head>セクションの最初に<base href="/">という記述を追加してください。このファイルが画面遷移の基準になります」

海未「app.module.tsRouterModuleをインポートして、ルーティングの定義を行います」

海未「このコードの以下の部分がルーティングの定義です」

海未「pathがURLに対応する部分です。この場合、ローカル開発環境ではhttp://localhost:3000/idolsにアクセスするとIdolsComponentが表示される、といった具合になります」
穂乃果「・・・表示されないよ?」
海未「気が早いですよ」

海未「そのIdolsComponentを表示する場所を用意しましょう。AppComponentのテンプレートを修正します」

海未「このようにrouter-outletタグを書いておくと、その部分にルータによって決定されたコンポーネントが表示されるようになります」
穂乃果「ほんとだ、今度は表示された!」
ことり「その上のrouterLinkは何なのかな?」
海未「それはルーティングの定義にあるpathを使って画面遷移を行うためのディレクティブです。書き方は普通のaタグのhrefrouterLinkに変わるだけですから、今のところは難しくないでしょう」


海未「それでは、Part2で示したような、トリオユニットを表示するダッシュボード画面を実装しましょう。dashboard.component.tsを作成して簡単な実装を入れておきます」

海未「app.module.tsDashboardComponentをインポートして、先ほどのルーティングの定義にダッシュボードを追加します」

海未「app.module.tsdeclarationsにもDashboradComponentを追加してください」
穂乃果「これで/dashboardにアクセスすると・・・うん、My Dashboardって表示されるね」
ことり「画面を追加するのは難しくないね」
海未「そうですね。これが一番基本的な方法ですが、これだけで十分という場合もあるでしょう」

海未「さて、このダッシュボード画面をスタートページに使いたいとします。つまり、特にパスを指定せずにアクセスされたらこのダッシュボードを表示するということです」
ことり「リダイレクト?」
海未「はい。ダッシュボードのURLはたとえばhttp://localhost:3000/dashboardですが、http://localhost:3000/へのアクセスもダッシュボードへリダイレクトさせたいとしましょう」

海未「このように定義を追加すると、パスの指定がなかった場合はダッシュボードへリダイレクトされるようになります」

海未「AppComponentのテンプレートにもリンクを追加しておきましょう」


海未「ではダッシュボードの画面を実装していきましょう。テンプレートの書き方を、これまでとは少し変えてみます」

海未「templateUrlを使うと、テンプレートを直接記述するのではなく外部のHTMLファイルを読み込むことができます」
ことり「テンプレートが大きくなると、別ファイルになってる方が読みやすそうだね」
穂乃果「そっか、普通のHTMLファイルならエディタでシンタックスハイライトも補完も効くねっ!」
ことり「Visual Studio Codeとかだとテンプレート直書きでも多少の補完は効くけどね・・・」
海未「そんなわけで、dashboard.component.htmlを作成します」

海未「書き方そのものはこれまでと変わりません。dashboard.component.tsの実装に進みましょう」

海未「まずは必要なクラスのインポートです。ここまでで出てきたものばかりですね」
穂乃果「IdolServiceはここでも使えるんだ」
ことり「providerの指定をIdolsComponentからAppModuleに移したんだったよね」

海未「続いてDashboardComponentの実装です」

海未「ユニットの選定ロジックは置いておきましょう。ひとまず、先頭3人を選ぶことにします」
ことり「選挙になるもんね・・・」
海未「あとは見覚えのあるコードだと思うので、大丈夫でしょう」
穂乃果「あ、ダッシュボードに穂乃果と海未ちゃんとことりちゃんが出てきたよっ!」


海未「ダッシュボードはこれで完成です。さて、今までアイドルの詳細は、一覧の下に表示していました。これを独立した画面にしてみましょう」
穂乃果「それって、えーと、ルーティングの定義を1つ増やせばいいだけじゃないの?IdolDetailComponentの」
ことり「穂乃果ちゃん、海未ちゃんにぶっぶーって言われるよ」
海未「言いません!・・・いえ、確かにぶっぶーなのですが」
穂乃果「駄目なんだ・・・」
海未「考えてみてください。IdolDetailComponentはアイドル1人の詳細を表示するものですが、誰を表示するかは外から指定しないといけません」
穂乃果「あ、そっか・・・今までは@Input()で受け取ってたんだっけ」
ことり「じゃあ、画面を表示するときにパラメータを受け取らないといけないね」
海未「そのためにパラメータ付きのルート定義を書くことができます」

海未「:idの部分がパラメータとして扱われます。/detail/11にアクセスすると、idが11の穂乃果が表示されるといったように」
ことり「URLの一部がパラメータになるんだね」

海未「ここで一旦ルーティングからそれて、IdolServiceIdolを取得するメソッドを用意しておきましょう。この後の実装で使います」

海未「もともとあったgetIdolsと大差はないですね。指定したidのアイドルを返すメソッドです」

海未「では、idol-detail.component.tsを修正していきましょう」

海未「インポートの部分はこのようになります。増えたものの詳細はこの後出てきますから、今は気にしなくてもよいでしょう。次に、必要なサービスをDIしておきます」

海未「以前と同様、データの取得はngOnInitで行います。OnInitインターフェイスを実装する必要がありましたね」

海未「ngOnInitの中身に進みましょう」

海未「これはこれまで出てこなかった内容ですね」
穂乃果「switchMapsubscribe?」
ことり「えーと、routeActivatedRouteなんだよね・・・でも、そもそもActivatedRouteって?」
海未「順番にいきましょう。ActivatedRouteparamsプロパティには、pathに付加されたパラメータが格納されています。今回のケースでは表示対象のアイドルのidが含まれています」
ことり「そこは、なんとなくわかるかな」
海未「switchMapは、端的に言うと関数を受け取ってその実行結果を返すというものです。ところで、IdolServicegetIdolPromiseを用いた非同期処理でしたね?」
穂乃果「さっき作ったやつだよね」
海未「非同期処理を伴う関数をswitchMapに渡した場合、最初の非同期処理の結果が返ってくる前に次の非同期処理がリクエストされると、最初の処理をキャンセルして次の処理を行う、という動きをします」
ことり「同時に1つしか処理しないんだ」
海未「今回のような初期化処理の場合、複数回取ってくる意味はありませんから・・・この、複数の処理をリクエストされた場合の挙動をコントロールするのに、flatMapconcatMapといったものもあります」
ことり「今回は最後のリクエストで、paramsからidを取り出し・・・あれ、この+って何かな?」
海未「Paramsの中身は全てstringですから、そこでnumberに変換してあげています」
ことり「そっか、getIdolの引数はnumberだったよね」

ことり「subscribeは?」
海未「switchMapの結果、この場合はIdolですが、それを引数に取る関数を渡すと、switchMapに渡した非同期処理が完了したタイミングで実行されます」
ことり「簡単に整理すると、Paramsに入ってるidでIdolを取ってきてidolプロパティに入れてる、ってことだね」


海未「ここまでで、ダッシュボード、一覧、詳細の各画面を表示できるようになりました」
穂乃果「できあがり?今回は長かったね・・・」
海未「まだです。特に詳細画面については、遷移のためのボタンやリンクが必要ですね。それに、templateUrlを使ったファイルの分割整理や、スタイルシートで見た目を整える作業もあります」
穂乃果「うえぇ」

海未「はじめに、詳細画面から1つ前の画面に戻る機能を用意しましょう。どこから来たかは特定できませんから、履歴から1つ戻すことにします。IdolDetailComponentに」

海未「1つ戻るメソッドを追加します。普通のJavaScriptで書く場合と似ていますね」
穂乃果「locationってなんだっけ」
ことり「穂乃果ちゃん、さっきインジェクションしたやつだよ」

海未「あとは、ボタンをクリックしたときにこれが呼ばれればよいので、テンプレートを修正します。この際、先ほどと同様HTMLファイルに切り出してしまいましょう」

海未「IdolDetailComponent@Componentはこうなります」


海未「さて、現在ダッシュボードには3人のアイドルが表示されていますが、それをクリックしたら詳細画面を表示したいところですね」
ことり「そうだね。一覧と同じ動き・・・あれ、一覧も、画面下じゃなくて詳細画面に表示しなきゃだよね」
海未「そうですね。一覧の方は後にして、まずダッシュボードを片付けましょう」

海未「ダッシュボードでは、アイドルの名前は<div>で表示していました。これを<a>に置き換えてリンクにします」

海未「routerLinkには配列でパラメータを渡すことができます。この例では/detailというパスにアイドルのidを付けて遷移する、という動きになります」
穂乃果「じゃあ動作確認担当の穂乃果だよっ!・・・うん、ちゃんとそれぞれの詳細画面が表示されるね」


海未「ここまでで、ルーティングの定義がだんだん増えてきました。と言ってもまだ4つですが、アプリケーションによってはこれがもっと多くなることは予想がつきますね」
ことり「app.module.tsがルーティングの定義で埋まっちゃいそう・・・」
海未「そこで、です。ルーティングの定義だけをまとめたモジュールを別に用意して、AppModuleの見通しをよくしましょう。app-routing.module.tsを作成します」

海未「インポートしたRouterModuleに対してルーティングの設定を行い、それをエクスポートしています。これで、このAppRoutingModuleをインポートすればここで設定したルーティングを適用できるようになります」

海未「app.module.tsはこうなります」
ことり「だいぶすっきりしたね」
海未「何事も分類整理は大切です。聞いていますか穂乃果」
穂乃果「あーあー、聞こえない、聞こえない~」
海未「次からイヤモニでも付けさせますか」
ことり「あれ、よく外れるんだよね・・・」


海未「では、先ほど話が出ましたが一覧画面の方も詳細画面へ遷移するようにします。ここでは一工夫加えてみましょう」
穂乃果「一工夫って?」
海未「一覧で誰かをクリックしたら、簡単な詳細を表示して、そこから詳細画面に遷移できるようにするのです」

海未「IdolsComponentのテンプレートをこのように修正します。これで、クリックしたアイドルの名前と、詳細画面へ遷移するボタンを表示することができました」
ことり「ちょっとわからないところがあるんだけど・・・」
海未「おや、ことりが珍しいですね」
ことり「データは全部小文字で登録してたよね? 今追加したところ、大文字で表示されるんだけど・・・」
海未「なるほど、それは{{selectedIdol.name | uppercase}}の部分が肝ですね。Angularにはパイプという機能があって、データに様々な加工を施せるのです」
ことり「えーっと、| uppercaseって書いてるのがパイプ?」
海未「はい。uppercaseは入力、つまり|の左側の文字列を全て大文字にするパイプです」
穂乃果「パイプって、他にも種類があるのかな?」
海未「uppercaseの逆のlowercaseや日付をフォーマットするdateなどいろいろありますし、自分で作ることも可能です」


海未「そろそろidols.component.tsも大きくなって扱いにくくなってきましたね。これも、テンプレートとスタイルシートを別ファイルに切り出してしまいましょう」

海未「IdolsComponentのメタデータはこうなります。styleUrlsは初めて出てきましたが、templateUrl同様読み込むファイルを指定するものです。注意点としては、styleUrlsは配列の形で複数ファイルを指定できる点ですね」


海未「では、詳細へ遷移するボタンを実装しましょう。テンプレートには(click)="gotoDetail()"とすでに書いてしまいましたので、このgotoDetailを実装します」

海未「このようになります。パラメータの渡し方はさきほど見たのと同じですね。ですが、これだけでは動きません」
穂乃果「routerがないね」
海未「まずはこれが必要ですね」

海未「それから、IdolsComponentのコンストラクタでインジェクションしてあげれば完成です」


海未「最後にCSSで見た目を整えましょう。ここからは、テンプレートとスタイルシートは別ファイルに切り出す前提で進めます。まずはIdolDetailComponentから」

海未「次にDashboardComponentです」

海未「AppComponentにもナビゲーションリンク部分がありますね」

海未「最後に、アプリケーション全体に適用されるグローバルスタイルを用意しましょう。appディレクトリの1階層上に、styles.cssがあると思いますので、内容を見比べつつ追記します」
ことり「QuickStartのバージョンアップで変わるかもしれないしね」


海未「長くなりましたがこれでおしまいです。ルーティングの話は半分くらいでしたが、これでアプリケーションとして動作するレベルになりました」
穂乃果「ふえぇ~、ほんとに長かったよ~」
ことり「OneNoteが悲鳴上げてる・・・」
海未「OneNote?」
ことり「ううん、こっちの話~♪」

海未「では次回は、いよいよHTTP通信に挑戦です。総仕上げですね」


LINEで送る
Pocket


返信を残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です