【SAPUI5】Routingとナビゲーション

##Routingとは
Routingは、SAPUI5アプリのナビゲーションで使う技術です。今回はRoutingの概念、実装について説明します。

Routingでは、ビューまたは複数のビューのまとまりをRouteと呼びます。
Routingとは、RouteからRouteへ移動する際に「どんなURLで」「どこのビューへ」遷移するのかを定義するものです。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/276021/becd8e0f-8e7b-35ac-1db2-8d86e1f7a4ae.png)

###ハッシュついて
Routeは固有のURLを持ちます。#の後にそれぞれのビューに固有のパラメータがつきます。この部分をハッシュと呼びます。以下のようなイメージです。
一覧画面 ...#/list
詳細画面 ...#/detail/0
URLが変わるので、ブラウザの履歴にそれぞれのURLが保持されます。このおかげでブラウザの「戻る」「進む」ボタンを使って前の画面に戻ったり、次の画面に進んだりすることが可能です。これはWebDynrpoやBSPにはない特徴です。

##Routingの実装
###ゴール
行を選択すると詳細画面に遷移するアプリを作ります。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/276021/86d48129-0d3c-6471-ac09-9d0521d82aa7.png)

![image.png](https://qiita-image-store.s3.amazonaws.com/0/276021/e499a997-47a4-8f9e-7ede-80b9caa66c82.png)

###構成
Routingのために実装する箇所は以下の4点です。
1. manifest.json
2. Component.js
3. 移動元ビューのコントローラー
4. 移動先ビューのコントローラー
![image.png](https://qiita-image-store.s3.amazonaws.com/0/276021/67863810-3954-69d5-3932-6b7a82426ecc.png)

###ステップ
前提：[入門編の記事まとめ](https://qiita.com/tami/items/53990a14e5ca919ec35a)

0.事前設定
1.Routingの定義
2.Routerを初期化
3.Routerのナビゲーションメソッドを呼び出し
4.選択された行に対するモデルをバインド

####0. 事前設定
モデル、ビュー、コントローラーを作ります。

Data.json

```json
{
	"Products": [
		{
			"name": "あずきもなか",
			"price": 105,
			"currency": "JPY",
			"ingredients": "牛乳、砂糖、小豆",
			"producer": "大納言農場"
		},
		{
			"name": "いちごアイス",
			"price": 120,
			"currency": "JPY",
			"ingredients": "牛乳、砂糖、いちご",
			"producer": "とよのかファーム"
		},
		{
			"name": "ヨーグルトアイス",
			"price": 160,
			"currency": "JPY",
			"ingredients": "ヨーグルト",
			"producer": "カスピ海工房"
		}		
	]
}
```
このモデルをデフォルトビューとしてmanifest.jsonに設定

```json
		"models": {
			"":{
				"type": "sap.ui.model.json.JSONModel",
				"uri": "model/Data.json"
			}
		}
```


App.view.xml（一覧画面）

```xml
<mvc:View 
	controllerName="template.controller.App" 
	xmlns="sap.m" 
	xmlns:mvc="sap.ui.core.mvc">
	<App id="idApp">
		<pages>
			<Page title="{i18n>pageTitle}">
				<content>
					<List 
						items="{/Products}" id="idList" headerText="アイスクリーム"
						selectionChange="onSelect" mode="SingleSelectMaster">
						<items>
							<ObjectListItem 
								type="Navigation"
								title="{name}" 
								number="{ 
									parts:[{path:'price'},{path:'currency'}],
									type: 'sap.ui.model.type.Currency',
									formatOptions: {showMeasure: false}
									}"
									numberUnit="{currency}">
							</ObjectListItem>
						</items>
					</List>				
				</content>
			</Page>
		</pages>
	</App>
</mvc:View>
```

Detail.view.xml（詳細画面）

```xml
<mvc:View controllerName="template.controller.Detail" xmlns="sap.m" xmlns:f="sap.ui.layout.form" xmlns:mvc="sap.ui.core.mvc"
	xmlns:core="sap.ui.core">
	<App id="idApp">
		<pages>
			<Page title="{i18n>detailPageTitle}">
				<content>
					<f:SimpleForm id="SimpleForm" editable="true" title="商品詳細">
						<f:content>
							<Label text="商品名"/>
							<Text id="name" text="{name}"/>
							<Label text="価格"/>
							<Text id="price" text="{price} {currency}"/>
							<Label text="原料"/>
							<Text id="ingredients" text="{ingredients}"/>	
							<Label text="生産者"/>
							<Text id="producer" text="{producer}"/>								
						</f:content>
					</f:SimpleForm>
				</content>
			</Page>
		</pages>
	</App>
</mvc:View>
```
####1. Routingの定義
manifest.jsonのsap.ui5セクションに以下のコードを追加します。

```json
	"sap.ui5": {
		"_version": "1.2.0",
		"rootView": {
			"viewName": "template.view.App",
			"type": "XML",
			"id": "app"
		},
		"routing": {
			"config": {
				"routerClass": "sap.m.routing.Router",
				"viewPath": "template.view",
				"viewType": "XML",
				"controlId": "idApp"
			},
			"routes": [
				{
					"pattern": "",
					"name": "root",
					"target": ["t_root"]
				},
				{
					"pattern": "detail/{index}",
					"name": "detail",
					"target": ["t_detail"]
				}				
			],
			"targets": {
				"t_root": {
					"viewName": "App",
					"viewId": "idViewApp",
					"controlAggregation": "pages"
				},
				"t_detail": {
					"viewName": "Detail",
					"viewId": "idViewDetail",
					"controlAggregation": "pages"
				}				
			}
		},
```


Routingは以下3つのセクションに分かれています。
今回使用しているパラメータは一例で、詳しくは以下のページをご参照ください。
[Routing Configuration](https://sapui5.hana.ondemand.com/#/topic/902313063d6f45aeaa3388cc4c13c34e)

**config**

- routerClass: 固定でsap.m.routing.Routerを指定
- viewPath: ビューのファイルが格納されているフォルダパス
- viewType: ビュータイプ
- controlId: ビューを表示するのに使われるコントロールのID。今回はAppコントロールを使用するので、Appコントロールのidである"idApp"を指定

※viewPath以下はtargetsのパラメータの初期値で、targets側で個別に定義することも可能
 
**routes**

- name: ルート名
- pattern: ハッシュを指定する。{ }で囲むことで変数も指定可能。今回の場合、indexに指定された値がハッシュの一部となる。（変数名は任意）
- target: targetsセクションで定義したtargetの値を指定する。Master Detail画面のように一つの画面に複数ビューを表示するときは複数指定する。

**targets**

- viewName: ビュー名
- viewId: ビューのID。ビュー側であらかじめ定義する必要はなく、ここで定義するだけでよい
- controlAggregation: どのaggregationにビューを追加するかを定義。今回はApp配下のpages aggregationに追加するので、pagesを指定

config, routes, targetsの関係
![image.png](https://qiita-image-store.s3.amazonaws.com/0/276021/f7cca5ec-6df9-45ff-f7ab-962cbcc10095.png)



####2. Routerを初期化
Component.jsの初期化メソッドの中で、Routerの初期化を行います。

```javascript
sap.ui.define([
	"sap/ui/core/UIComponent"
], function (UIComponent) {
	"use strict";

	return UIComponent.extend("template.Component", {

		metadata : {
			manifest: "json"
		},

		init : function () {
			// call the init function of the parent
			UIComponent.prototype.init.apply(this, arguments);

			// Routerを初期化
			var oRouter = this.getRouter();
			oRouter.initialize();
		}

	});
});
```
####3. Routerのナビゲーションメソッドを呼び出し
Appビュー（一覧を表示）の中で、以下の処理を実装します。
①Routerオブジェクトを取得

App.controller.js

```javascript
sap.ui.define([
	"sap/ui/core/mvc/Controller",
], function (Controller) {
   "use strict";
   return Controller.extend("template.controller.App", {
   		onInit: function(){
   			this.oRouter = this.getOwnerComponent().getRouter();
   		}
});		
```
初期処理の中で、Routerオブジェクトを取得しておきます。

②行が選択されたときに呼ばれるメソッドを実装

```javascript
   		onSelect: function(oEvent){
   			//選択された行のパスを取得：/Products/2 のような形で取得される
   			var sPath = oEvent.getParameter("listItem").getBindingContextPath();
   			//indexに上記パスからインデックスの部分を取得
   			var index = sPath.split("/")[sPath.split("/").length - 1];
   			//Routerオブジェクトを使って詳細画面へ遷移する
   			this.oRouter.navTo("detail", {
   				index: index
   			});
   		}
```
RouterオブジェクトのnavToというメソッドを使って、detailに移動します。
detailというのはroutingの設定で定義したrouteの名前です。detailのpatternは以下のようになっていました。
`"pattern": "detail/{index}"`
変数indexを引数に取りますよ、と宣言しているのでnavToメソッドにindexを渡します。

####4. 選択された行に対するモデルをバインド
Detailビューでは、一覧で指定された行に対応する商品の詳細を表示すします。このために、引数に渡された値をもとにモデルへのパスを作ってビューにバインドします。以下の処理を実装します。
①Routerオブジェクトを取得（Appと同様）
②RoutePatternMatchedイベントをイベントハンドラを登録

Detail.controller.js

```javascript
sap.ui.define([
	"sap/ui/core/mvc/Controller",
], function (Controller) {
   "use strict";
   return Controller.extend("template.controller.Detail", {
   	
   		onInit: function(){
   			//Routerオブジェクトを取得
   			this.oRouter = this.getOwnerComponent().getRouter();
   			//ルートがマッチしたときのイベントハンドラを登録
   			this.oRouter.attachRoutePatternMatched(this._matched, this);
   		} 
   });
});	
```
RoutePatternMatchedイベントは、実行されたURLのハッシュ部分がRoutingの定義の中で定義されたいずれかのパターンに一致した場合に発火します。パターンにマッチした場合はこのあと定義する_matchedメソッドが呼ばれます。
一覧画面から遷移する場合だけでなく、直接このURLが実行された場合でもRoutePatternMatchedがトリガされます。

③_matchedの実装

```javascript
   		_matched: function(oEvent){
   			//呼び出し元画面で指定された引数を取得
   			var index = oEvent.getParameter("arguments").index;
   			//パスを作成：/Products/2のような形に
   			var sPath = "/Products/" + index;
   			//パスをビューにバインド
   			this.getView().bindElement(sPath);
   		}
```

##実行結果
アプリを実行します。ここでは、アドレスバーに注目してください。
一覧画面ではハッシュはついていません。一覧画面のrouteのパターンが""となっているためです。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/276021/5bb72afb-4a23-73e4-4b37-78f4bf0f9cc2.png)

詳細画面では、#のあとに/detail/0と表示されます。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/276021/85473b46-2f52-8338-d4d4-6d96ef3dfd2d.png)

ここで、0を1に変えてみます。
すると、2番目の商品の詳細が表示されました。このように直接URL指定でもビューを切り替えることができます。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/276021/36be0693-7d1b-2873-f7a1-305f6dbbf553.png)

##まとめ

- SAPUI5では、ナビゲーションにRoutingを使う
- Routingの定義は、config, routes, targetsからなる
- ハッシュが変わることで、ブラウザの履歴を使ったナビゲーションが可能になる