【SAPUI5】ODataV2ModelのContext、Bindingをイメージで理解する

##1. はじめに
SAPUI5でOData操作をするときに、ContextやBindingといった言葉がよく出てきます。getBinding, getBindingContext...などなど。サンプルソースを見ながら書けば動作するものの、一体自分は何をやっているのかいまいちイメージできなくないでしょうか？
そこで今回は、**Context、Bindingをイメージで理解する**ことをテーマに記事を書きました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276021/d704b556-04be-4942-0784-756f55ec7c15.png)


##2. ODataモデル、Context、Bindingの関係
###2-1. ODataモデル
SAPUI5のアプリケーションを作るときに、manifest.jsonでデフォルトのODataモデルを定義します。ODataモデルは、**バックエンドのデータソースとアプリケーションをつなぐトンネルのような役割**です。ODataモデルのおかげで、アプリケーションからデータソースにアクセスするための道ができます。
一方、画面のコントロール（Input, Form, Tableなど）にデータを表示するためには、もっと具体的に「どのデータを表示するか」の情報が必要です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276021/c6e7ffd2-8fba-a833-dc90-dcc938d35321.png)

###2-2. Context
そこでContextが登場します。Contextは、**ODataモデルの中にあるオブジェクトへのポインター**です。上の図の例でいうと、「ProductsのID：10番のデータ」といった具合に、具体的なデータを指します。Contextを与えることによってコントロールはデータを表示することができます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276021/5ca525f4-e252-3eb8-3286-9cbd32e01349.png)

###2-3. Binding
続いて、Bindingについて見ていきます。BindingはContextを進化させた役割を持っています。

- モデルに関連するイベントの発生をコントロールに知らせる、イベントプロバイダーの役割
- モデルから取得するデータを細かく指定するため、パラメータ（expand, selectなど）、フィルタ、ソートを設定する役割

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276021/81902a5b-8732-99df-23c1-24ee78da4550.png)

以降でContext、Bindingの使い方について見ていきます。

##3. Contextの使い方
###3-1. Contextをコントロールに割り当てる方法

```javascript
//Contextを作る
var oContext = oModel.createBindingContext("/Products('10')");
//コントロールに割り当てる
var oForm = this.byId("form");
oForm.setBindingContext(oContext);
```

###3-2. コントロールに割り当てられたContextを取得する方法

```javascript
var oForm = this.getView().byId("form");
var oContext = oForm.getBindingContext();
```

###3-3. Contextを使って何ができるか
Contextが指しているデータのパスを取得したり（getPath）、データそのものを取得したり（getObject）することができます。

```javascript
//Contextが指しているデータのパスを取得
oContext.getPath(); //"/Products('10')"

//Contextが指しているデータを取得
oContext.getObject(); //{__metadata: {…}, ProductId: "10", Name: "Apple", ...}

//データの中から特定の項目を取得
oContext.getProperty("Name"); //"Apple" 
```
参考：sap.ui.model.Contextの[APIリファレンス](https://openui5.hana.ondemand.com/api/sap.ui.model.Context#methods/Summary)

##4. Bindingの使い方
###4-1. Bindingの種類
Bindingには以下の種類があります。TreeBindingというものもありますが、ここでは割愛します。コンテキストをコントロールのどの部分に設定するかによって呼び方が変わります。

- PropertyBinding：コンテキストをコントロールのプロパティ（"text"や"value"プロパティ）に割り当てる
- ContextBinding：コンテキストをコントロールの"binding"プロパティに割り当てる。子のコントロールは相対パスでデータにアクセスできる
- ListBinding：コンテキストをTableなどのリストの"items"や"rows"プロパティに割り当てる

###4-2. Bindingをコントロールに割り当てる方法

####4-2-1. PropertyBinding
ビューでバインドする方法

```xml
<Text id="idText" text="{ProductId}"/>
<Input id="idInput" value="{Name}"/>
```

コントローラーでバインドする方法

```javascript
/*構文*/
//sNameにはバインド先のプロパティを設定します
bindProperty(sName, oBindingInfo) 

//Textの場合
var oText = this.byId("idText");
oText.bindProperty("text", {path: "ProductId"}); 

//Inputの場合
var oInput = this.byId("idInput");
oInput.bindProperty("value", {path: "Name"}); 
```

####4-2-2. ContextBinding
ビューでバインドする方法

```xml
<!--※実際には、コントローラーでProductIDを特定する必要がある-->
<SimpleForm id="idForm" binding="{/Products}">  
    <content>
        <!--ここはPropertyBinding-->
        <Text id="nameText" text="{ProductId}"/> 
    <content/>
</SimpleForm>
```

コントローラーでバインドする方法

```javascript
/*構文*/
//その1
bindElement(vPath, mParameters?)
//その2（使い方は同じ）
bindObject(vPath, mParameters?) 

var oSimpleForm = this.getView().byId("idForm");
var sPath = "/Products('10')"; //Keyは実際にはRouterなどから取得する
oSimpleForm.bindElement({
    path: sPath,
    //イベントハンドラを追加することもできる
    change: function () {
      //処理
    },
    dataReceived: function () {
      //処理
    },
    dataRequested: function () {
      //処理
    }
});
```

####4-2-3. ListBinding
ビューでバインドする方法

```xml
<Table id="idTable" items="{/Products}">
	<columns>
		<Column>
			<Text text="ProductID"/>
		</Column>
		<Column>
			<Text text="Name"/>
		</Column>
		<Column>
			<Text text="Price"/>
		</Column>
	</columns>
	
	<ColumnListItem id="columnListItem">
		<cells>
			<Text text="{ProductID}"/>
			<Text text="{Name}"/>
			<Text text="{Price}"/>
		</cells>
	</ColumnListItem>
</Table>
```

コントローラーでバインドする方法

```javascript
/*構文*/
//その1 ListBaseを継承しているコントロールの場合
bindItems(oBindingInfo)
//その2 ジェネリックなやり方
//sNameにはバインド先のプロパティを設定します item, rowsなど
bindAggregation(sName, oBindingInfo) 

//sap.m.Tableにバインドする場合
var oTable = this.byId("idTable");
var oTemplate = new ColumnListItem({
    cells: [
		new Text({
		    text: "{ProductID}"
		}),
		new Text({
		    text: "{Name}"
		}),
		new Text({
		    text: "{Price}"
		})
    ]
});

var sPath = "/Products";
oTable.bindItems({path: sPath, template: oTemplate});
//または
oTable.bindAggregation("items", {path: sPath, template: oTemplate});
```

###4-3. コントロールに割り当てられたBindingを取得する方法
####4-3-1. Property, List Bindingの場合

```javascript
/*構文*/
//Property / List Binidngの場合
oControl.getBinding(sName) //sNameにはバインド先のプロパティを設定します

//PropertyBinding
var oText = this.byId("idText");
var oPropertyBinding = oText.getBinding("text");

//ListBinding
var oTable = this.byId("idTable");
var oListBinidng = oTable.getBinding("items");
```
####4-3-2. Element Bindingの場合
Element Bindingの場合、oControl.getBinding("binding")とやってもBindingは取得できません。以下のメソッドを使います。

```javascript
/*構文*/
//その1
oControl.getObjectBinding()
//その2
oControl.getElementBinding()

var oForm = this.byId("idForm");
var oElementBinding = oForm.getObjectBinding();
```

###4-4. Bindingを使って何ができるか
Bindingを使ってできることはたくさんあるので、全体感がわかるように図にしてみました。PropertyBinding, ContextBinding ListBindingはすべてsap.ui.model.Bindingというクラスを継承しています。Bindingクラスではイベントハンドラの設定やモデルに対する操作ができて、この機能は各サブクラスに引き継がれています。さらに、サブクラスには独自の機能も実装されています。（例えばリストではfilter, sortなど）
図には一部の機能しか載せていないので、必要になったら[APIリファレンス](https://sapui5.hana.ondemand.com/#/api)を参照してみてください。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276021/d9f73f1d-79b1-8460-65f6-c2fbbc93aba2.png)

##5. まとめ
以上のように見てきて、普段自分ががコントロールとモデルをつなげるためにやっていることのほとんどは、Bindingなのだとわかりました。ContextはBindingの中にあって、データへのポインタとして働いている…そんなイメージでよいのではないでしょうか。
最後に、このソースは何をしているのかな？と思ったときの簡単な見分け方は以下です。

- **bindxxx** : Bindingをセットしている　→　コントロールとモデルをつなげているんだな！
- **getxxxBinding** : Bindingを取得している → Bindingを使った操作をしようとしているんだな！（ソート、フィルタ、イベントハンドラなど）
- **setxxxContext** : Contextをセットしてる → コントロールとモデルをつなげているんだな！
- **getxxxContext** : Contextを取得している → Contextを使った操作をしようとしているんだな！（モデルのデータを取得するなど）