2
Help us understand the problem. What are the problem?

More than 1 year has passed since last update.

posted at

updated at

【SAPUI5】ODataV4を触ってみる

はじめに

こんにちは。
SAPUI5にてODataV4を触る機会があったので
備忘録としてCRUD操作などの方法を記事として書かせていただきます。

前提

以下の記事を参考にODataサービスを作成し、そのサービスをODataV4として使用します。
今回はUIモジュールを適当に作成しそこで上記のサービスを触っていきます。
【CAP】CAPで作ったODataをUI5アプリで使う(1) CAPサービスの作成

manifest.jsonを変更する

manifest.json
{
    "_version": "1.21.0",
    "sap.app": {

        // 〜省略〜

        "dataSources": {
            "capServicedevices": {
                "uri": "/capService/cap-mock/",
                "type": "OData",
                "settings": {
                    "localUri": "localService/capService/cap-mock/metadata.xml",
                    "odataVersion": "4.0"
                }
            }
        }
    },
    "sap.ui": {
        "technology": "UI5",
        "icons": {
            "icon": "",
            "favIcon": "",
            "phone": "",
            "phone@2": "",
            "tablet": "",
            "tablet@2": ""
        },
        "deviceTypes": {
            "desktop": true,
            "tablet": true,
            "phone": true
        }
    },
    "sap.ui5": {

        // 〜省略〜

        "models": {
            "oDataDevicesModel": {
                "type": "sap.ui.model.odata.v4.ODataModel",
                "settings": {
                    "synchronizationMode": "None",
                    "operationMode": "Server",
                    "earlyRequests": true,
                    "groupProperties": {
                        "default": {
                            "submit": "Auto"
                        }
                    },
                    "groupId": "$auto"
                },
                "dataSource": "capServicedevices"
            }
        }

        // 〜省略〜

    }
}

manifest.jsonのdataSourcesとmodelsに使用するODataサービスの設定を記載します。


synchronizationMode

バインディング間の同期を制御するものだそうです。 'None'以外の値はまだサポートされていないらしいので'None'を指定してください。

operationMode

フィルタリングとソートの操作モードの設定値です。'Server'を設定することでODataサービスリクエストのサーバーで、対応するURLパラメーター( &filter、&orderby)を追加することによって操作が実行されるようになるそうです。

earlyRequests

リクエスト要求の送る開始を高速化する?オプションだそうです。

groupProperties

アプリケーショングループのバッチリクエストの使用を制御する設定値です。詳しくはsap.ui.model.odata.v4.SubmitModeのリファレンスを参照ください。今回はいったんAutoにしておきます。

groupId

モデルによるバッチリクエストの使用を制御する設定値です。'\$auto'または'\$direct'を設定します。'\$auto'だとバッチリクエストを使用すするようになり、'\$direct'だとバッチを使用しない設定になります。

テーブルを作成しODataをバインドさせる

Main.view.xml
        <content>
          <Toolbar>
            <Button text="reload" press="onloadData"></Button>
            <Button text="create" press="onCreate"></Button>
            <Button text="delete" press="onDelete"></Button>
            <Button text="update" press="onUpdate"></Button>
          </Toolbar>
          <SearchField id="searchField" value="" search="onSearch"></SearchField>
          <Table id="deviceTable" mode="SingleSelectLeft" items="{
                        path: 'oDataDevicesModel>/Devices',
            parameters: {
                                    $count: true,
                                    $$updateGroupId : 'deviceGroup'
                                }
                    }" class="sapFDynamicPageAlignContent" width="auto">
            <columns>
              <Column id="column1">
                <Text text="{i18n>IMEI}"/>
              </Column>
              <Column id="column2">
                <Text text="{i18n>deviceModel}"/>
              </Column>
            </columns>
            <items>
              <ColumnListItem id="_IDGenColumnListItem1" type="Navigation">
                <cells>
                  <Input editable="false" value="{oDataDevicesModel>IMEI}" />
                  <Input editable="false" value="{oDataDevicesModel>deviceModel}" wrapping="false"/>
                </cells>
              </ColumnListItem>
            </items>
          </Table>
        </content>
        <footer>
          <OverflowToolbar>
            <ToolbarSeparator/>
            <Button text="save" press="onSave"></Button>
            <Button text="cancel" press="onCancel"></Button>
          </OverflowToolbar>
        </footer>

メインのページのコンテンツ部分にODataを呼び出すTableを設置します。
parametersに設定している$$updateGroupIdは任意のものを指定してください。
登録、更新、削除、検索処理を追加するために各種ボタンとSearchFieldを上につけています。

検索処理

Main.controller.js
        onSearch: function() {
            var oView = this.getView(),
                sValue = oView.byId("searchField").getValue(),
                oFilter = new Filter("deviceModel", FilterOperator.Contains, sValue);

            oView.byId("deviceTable").getBinding("items").filter(oFilter, FilterType.Application);
        },

検索処理を定義します。
バインド情報を取得しそこに対してfilterをかけることで検索処理をおこないます。

登録処理、更新処理

スクリーンショット 2021-05-12 18.20.38.png

Main.controller.js
        onCreate: function() {
            var oBinding = this.byId("deviceTable").getBinding("items");
            var oContext = oBinding.create({
                "IMEI": "",
                "deviceModel": ""
            });
            this._switchTableEditable(true);
        },
        _switchTableEditable: function(editable) {
            var table = this.byId("deviceTable");
            table.getItems().forEach(function(item) {
                for (var i = 0; i < item.getCells().length; i++) {
                    item.getCells()[i].setEditable(editable)
                }
            })
        },

上記の画像のように登録ボタンを押下した際にテーブルに入力欄を追加したいので
対象モデルに1行データを追加し、テーブルを編集可能にします。
ちなみにこのとき$$updateGroupIdやmanifest.jsonで指定したgroupIdを$directに設定している場合は
バッチを使用せずoBinding.createの時点で空のデータが作成されるリクエストが投げられるようになります。

さらにsaveボタン押下時の処理を定義していきます。

Main.controller.js
        // saveボタン押下時の処理
        onSave: function() {
            var self = this;
            var fnSuccess = function() {
                MessageToast.show(self._getI18nText("changesSentMessage"));
                self._setUIChanges(false);
                this._switchTableEditable(false);
            }.bind(this);

            var fnError = function(oError) {
                self._setUIChanges(false);
                MessageBox.error(oError.message);
                this._switchTableEditable(false);
            }.bind(this);

            this.getView().getModel("oDataDevicesModel").submitBatch("deviceGroup").then(fnSuccess, fnError);
            this._bTechnicalErrors = false; // If there were technical errors, a new save resets them.
        },
        _setUIChanges: function(bHasUIChanges) {
            if (this._bTechnicalErrors) {
                bHasUIChanges = true;
            } else if (bHasUIChanges === undefined) {
                bHasUIChanges = this.getView().getModel("oDataDevicesModel").hasPendingChanges();
            }
        },
        // cancelボタン押下時の処理
        onCancel: function() {
            // 変更部分をリセット
            this.byId("deviceTable").getBinding("items").resetChanges();
            this._bTechnicalErrors = false;
            this._setUIChanges();
            this._switchTableEditable(false);
        },

this.getView().getModel("oDataDevicesModel").submitBatch("deviceGroup")
の部分でバッチ処理を開始しモデルデータの変更部分に対して登録、更新リクエストを送ります。

hasPendingChanges()
はサーバに送信されていない変更部分がある場合はtrueを返すメソッドです。

cancelボタン押下時(onCancel()内)にはテーブルに加えた変更部分をリセットするために
this.byId("deviceTable").getBinding("items").resetChanges();
にて加えた変更をリセットします。

スクリーンショット 2021-05-12 18.43.21.png
適当に値を入力し、saveボタンを押下すると
スクリーンショット 2021-05-12 18.44.35.png
無事にバッチリクエストが投げられ、データの登録が行われました!

スクリーンショット 2021-05-12 18.54.23.png

更新処理も同じように実装します。

Main.controller.js
        onUpdate: function() {
            this._switchTableEditable(true);
        }

更新処理に関してもボタンが押下された際にテーブルを編集可能にし、saveボタン押下時に更新処理を行うよう処理をしたいので
更新ボタン押下時にテーブルの編集を可能にします。
保存ボタン押下時の処理は登録機能と同じく上記のonSaveの関数処理を呼び出すことで更新処理を行ってくれます。
テーブル内のバインドデータの変更部分を自動的に処理を行ってくれるのでV2のようにcreate,updateなどメソッドを分ける必要はありません。

先ほど追加したデータを更新してみます。
スクリーンショット 2021-05-12 18.55.54.png

先ほどのデータのdeviceModel部分を変更し、保存ボタンを押下すると

スクリーンショット 2021-05-12 18.56.21.png
バッチリクエストが送られ、データの更新が行われます。
スクリーンショット 2021-05-12 19.00.19.png

削除処理

削除処理についても実装していきます。

Main.controller.js
        onDelete: function() {
            var oSelected = this.byId("deviceTable").getSelectedItem();

            if (oSelected) {
                oSelected.getBindingContext("oDataDevicesModel").delete("$auto").then(function() {
                    MessageToast.show(this._getI18nText("mDeletionSuccessMessage"));
                    this._switchTableEditable(false);
                }.bind(this), function(oError) {
                    MessageBox.error(oError.message);
                    this._switchTableEditable(false);
                }.bind(this));
            }
        },

テーブルで選択されている行のBindingContext情報を取得し、削除処理を行います。
oSelected.getBindingContext("oDataDevicesModel").delete("\$auto")
の\$auto部分を\$directに変更することでバッチを使用せずそのままリクエストを行うように変更できます。

登録、更新の実装時に追加したデータを削除します。
対象の行を選択し、削除ボタンを押下すると
スクリーンショット 2021-05-12 19.11.02.png
無事リクエストが送られ、データが削除されました。

スクリーンショット 2021-05-12 19.12.14.png

更新処理などでBatchを使わずにリクエストする方法

更新処理、登録処理などをバッチを使用せずリクエスト送信したい場合はテーブルにて指定している$$updateGroupIdの値を'\$direct'に変更します。

Main.view.xml
<Table id="deviceTable" mode="SingleSelectLeft" items="{
                        path: 'oDataDevicesModel>/Devices',
            parameters: {
                                    $count: true,
                                    $$updateGroupId : '$direct'
                                }
                    }" class="sapFDynamicPageAlignContent" width="auto">

上記のように変更し、更新処理を行うと
スクリーンショット 2021-05-12 19.24.26.png
Batchが使用されていないことが確認できます。

まとめ

登録、更新、削除についてODataV4サービスでの基本的な実装ができることが確認できました。
個人的に更新時にPATCHリクエストではなく、PUTに指定しリクエストを行う方法がないのか気になっています。
そちらも調査を継続し、方法を発見した場合追記を行いたいと思います。

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Sign upLogin
2
Help us understand the problem. What are the problem?