【CAP】CAPで作ったODataをUI5アプリで使う(3) V4版Elementsアプリの作成 

##はじめに
この記事は、「[CAPで作ったODataをUI5アプリで使う](https://qiita.com/tami/items/d1b69a93da14e288faf2)」シリーズの3回目です。
前回の記事ではCAPのODataサービスをフリースタイルのUI5アプリから利用しました。今回は、Fiori Elementsアプリから利用する方法について調べてみたいと思います。
[前回](https://qiita.com/tami/items/55153c92fffc159e40d8)の記事と同様に、CAPのプロジェクトとは別のプロジェクトでFiori elementsアプリを作成します。

作成にあたり、以下のGitリポジトリを参考にしました。ここでは、V4のODataサービスをV4のままElementsアプリにしていたので、今回はその方法を踏襲したいと思います。
https://github.com/SAP-samples/cloud-cap-samples/tree/master/fiori

##ゴール
Fiori ElementsでList Reportを作成します。まずは簡単に、リストのみの表示です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276021/dc6f5d9b-361c-d035-6920-4cee38005a74.png)

##ステップ
1. CAPサービスにUIアノテーションを追加
2. Elementsプロジェクトを作成

##1. CAPサービスにUIアノテーションを追加
CAPサービスの作成方法については、[1回目](https://qiita.com/tami/items/d1b69a93da14e288faf2)の記事をご参照ください。product-servece.cdsにUIアノテーションを追加します。

```srv/products-service.cds
annotate ProductService.Products with @(
    UI: {
        // Filters
        SelectionFields: [ product,  description ],
        // Table columns
        LineItem: [
            {$Type: 'UI.DataField', Value: product},
            {$Type: 'UI.DataField', Value: description}              
        ]
    }
);

annotate ProductService.Products with {
    // Labels
    product @( Common.Label: 'Product ID');
    description @( Common.Label: 'Description' );
}
```

全体は以下のようになります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276021/57859124-88d3-9f45-247b-a069dd2e2a42.png)

`cds watch`を実行して、Fioriのプレビューを見てみましょう。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276021/4b8f4f23-c77e-a347-7165-5b07af6edd97.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276021/085ff174-6039-6b72-3a4c-cefdfffea8ef.png)

※一度Cloud Platformにデプロイした後では、package.jsonの中のdbの種類が"hana"になっているかもしれません。この状態だとローカルのDBが使えないので、一時的にsqliteに変更します。デプロイするときに元に戻します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276021/2ba1b5e2-b448-4ec4-0a46-07f07ea61821.png)

問題なければ、ビルド&デプロイします。


##2. Elementsプロジェクトを作成
###2.1. プロジェクトを作成

以下のコマンドでプロジェクトを作成します。

```
yo easy-ui5
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276021/be8896e1-08c9-26c3-9bce-610ed0a31e31.png)

###2.2. App Routerの設定
approuterのroutesにCAPのODataサービスの設定を追加します。destination"cap-products"はCloud FoundryサブアカウントのDestinationに登録済です。（[前回](https://qiita.com/tami/items/55153c92fffc159e40d8#cloud-foundry%E3%81%AE%E3%82%B5%E3%83%96%E3%82%A2%E3%82%AB%E3%82%A6%E3%83%B3%E3%83%88%E3%81%ABdestination%E3%82%92%E7%99%BB%E9%8C%B2)の記事を参照）

```json:approuter/xsapp.json
{
  "welcomeFile": "demoproductselements/",
  "authenticationMethod": "none",
  "logout": {
    "logoutEndpoint": "/do/logout"
  },
  "routes": [
    {
      "source": "^/cap-products/(.*)$",
      "target": "$1",
      "authenticationType": "xsuaa",
      "destination": "cap-products",
      "csrfProtection": false
    }    
  ]
}
```

ローカルでテストできるように、ui5.yamlファイルにもdestinationの設定を追加します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276021/9dc879c8-e1fa-0e7b-a4a8-cd23db8d72cd.png)

##3. Elementsアプリの設定
Elementsアプリはフリースタイルのアプリとは作りが違うので、webappフォルダの中身を消してしまいます。（xs-app.jsonは残す）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276021/6e6165bc-443a-189e-51a5-642144637705.png)

###3.1. Fiori Launchpadサンドボックス画面を作成
アプリを起動したときに表示する画面として、Fiori Launchpadサンドボックス画面を作成します。

```html:uimodule/webapp/index.html
<!DOCTYPE html>
<html>
<head>

	<meta http-equiv="X-UA-Compatible" content="IE=edge" />
	<meta http-equiv="Content-Type" content="text/html;charset=UTF-8" />
	<meta name="viewport" content="width=device-width, initial-scale=1.0" />
	<title>Bookshop</title>

	<script>
		window["sap-ushell-config"] = {
			defaultRenderer: "fiori2",
			applications: {
				"browse-products": {
					title: "Browse Products",
					description: "list",
					additionalInformation: "SAPUI5.Component=demo.productselements",
					applicationType : "URL",
					url: ".",
					navigationMode: "embedded"
				}
			}
		};
	</script>

	<script src="https://sapui5.hana.ondemand.com/test-resources/sap/ushell/bootstrap/sandbox.js"></script>
	<script src="https://sapui5.hana.ondemand.com/resources/sap-ui-core.js"
    	data-sap-ui-libs="sap.m, sap.ushell, sap.collaboration, sap.ui.layout"
    	data-sap-ui-compatVersion="edge"
    	data-sap-ui-theme="sap_fiori_3"
    	data-sap-ui-frameOptions="allow"
	></script>
	<script>
		sap.ui.getCore().attachInit(()=> sap.ushell.Container.createRenderer().placeAt("content"))
	</script>

</head>
<body class="sapUiBody" id="content"></body>
</html>
```

###3.2. manifest.jsonを作成
manifest.jsonファイルを作成します。

```json:uimodule/webapp/manifest.json
{
	"_version": "1.8.0",
	"sap.app": {
		"id": "demo.productselements",
		"type": "application",
		"title": "{{appTitle}}",
		"description": "{{appDescription}}",
		"i18n": "i18n/i18n.properties",
		"dataSources": {
			"/cap-products/product/": {
				"uri": "/cap-products/product/",
				"type": "OData",
				"settings": {
					"odataVersion": "4.0",
					"localUri": "localService/cap-products/product/metadata.xml"
				}
			}
		},
		"-sourceTemplate": {
			"id": "ui5template.basicSAPUI5ApplicationProject",
			"-id": "ui5template.smartTemplate",
			"-version": "1.40.12"
		}
	},
	"sap.ui5": {
		"dependencies": {
			"libs": {
				"sap.fe.templates": {}
			}
		},
		"models": {
			"i18n": {
				"type": "sap.ui.model.resource.ResourceModel",
				"uri": "i18n/i18n.properties"
			},
			"": {
				"settings": {
					"synchronizationMode": "None",
					"operationMode": "Server",
					"autoExpandSelect": true,
					"earlyRequests": true,
					"groupProperties": {
						"default": {
							"submit": "Auto"
						}
					}
				},
				"dataSource": "/cap-products/product/"
			}
		},
		"routing": {
			"routes": [{
				"pattern": ":?query:",
				"name": "ProductsList",
				"target": "ProductsList"
			}, {
				"pattern": "Products({key}):?query:",
				"name": "ProductsDetails",
				"target": "ProductsDetails"
			}],
			"targets": {
				"ProductsList": {
					"type": "Component",
					"id": "ProductsList",
					"name": "sap.fe.templates.ListReport",
					"options": {
						"settings": {
							"entitySet": "Products",
							"navigation": {
								"Products": {
									"detail": {
										"route": "ProductsDetails"
									}
								}
							}
						}
					}
				},
				"ProductsDetails": {
					"type": "Component",
					"id": "ProductsDetails",
					"name": "sap.fe.templates.ObjectPage",
					"options": {
						"settings": {
							"entitySet": "Products"
						}
					}
				}
			}
		},
		"contentDensities": {
			"compact": true,
			"cozy": true
		}
	},
	"sap.ui": {
		"technology": "UI5",
		"fullWidth": false
	},
	"sap.fiori": {
		"registrationIds": [],
		"archeType": "transactional"
	}
}
```
i18nフォルダも作成しておきます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276021/0562b7de-09c7-54d6-4293-d0c22277f794.png)

```i18n.properties
title=demo.productselements
appTitle=demo.productselements
appDescription=Products Elements
```


###3.3. Component.jsを作成
Component.jsファイルを作成します。

```javascript:uimodule/webapp/Component.js
sap.ui.define(["sap/fe/core/AppComponent"], function(AppComponent) {
  "use strict";

  return AppComponent.extend("demo.productselements.Component", {
    metadata: {
      manifest: "json"
    }});
});
```

###3.4. ローカルで実行
以上で設定は完了です。`npm start`コマンドを入力して、アプリを起動してみましょう。
まず、ラウンチパッド画面が開きます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276021/7843955b-3d3d-0e2a-d087-878be181b014.png)
タイルをクリックするとList Report画面が開きます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276021/7a0e8eaf-44cc-b5ed-1301-cb976becc0be.png)
行を選択するとObject Pageに遷移しますが、まだObject Page用のアノテーションを入れていないので何も表示されません。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276021/b27dac10-8df9-c482-40f5-b9173951b025.png)

####気づき
manifest.jsonのroutingの設定で、ProductsListのパターンが`"pattern": ":?query:"`となっていました。これは、任意のパラメータとしてクエリパラメータが付加される可能性があることを示しています。
なぜこれが必要かというと、Object Pageから一覧に戻ったときに、選択画面の状態を保存するためのsap-iapp-stateというパラメータが追加されるからです。もし`"pattern": ""`としていた場合、Object Pageから戻ったときにパターンがマッチせず、一覧画面が表示されなくなります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276021/32fa8457-6703-91e4-0b3c-8deea0cbf92e.png)

##追加情報
CAPでFioriのプレビューを表示するとき、裏でmanifest.json相当の設定が自動生成されています。今回作成したmanifest.jsonファイルは基本的に自動生成された設定と同じになっています。
興味のある方は、CAPプロジェクトのnode_modules/@sap/cds/lib/utils/app/fiori-preview.jsを見てみてください。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276021/e4fd3484-c752-0f5d-b1cd-f402f015adeb.png)
参考：[Ep.31 – Digging into CAP’s fiori-preview](https://www.youtube.com/watch?v=LSY_hcTHzGw&list=PL6RpkC85SLQAIntm7MkNk78ysDm3Ua8t0&index=32)

##今後やってみたいこと
- OData V2でCAPを使用したElementsアプリを作成する
- ローカルアノテーションを追加してみる
- [Fiori Tools](https://blogs.sap.com/2020/06/22/sap-fiori-tools-is-generally-available.-increase-the-efficiency-of-developing-sap-fiori-elements-apps/)を使ってCAPのフロントエンドを作成できるか