SAPUI5 (38) - OData Model 的單向綁定和雙向綁定

單向綁定和雙向綁定概述

所謂的單向綁定 (one-way binding),是指 OData model 與 UI 控件之間在數(shù)據(jù)綁定時, OData model 中數(shù)據(jù)的變化會同步反應(yīng)在 UI 控件中,但 UI 控件數(shù)據(jù)的變化需要手工提交到 OData model;而雙向綁定 (two-way binding),則是指 OData model 和 UI 控件的數(shù)據(jù)雙向同步變化。

OData (v2) model 默認(rèn)的是 one-way binding。OData model 剛開始只支持單向綁定,后來也支持 two-way binding。

使用 one-way 或者 two-way binding,可以進(jìn)一步簡化客戶端代碼的編寫,同時對 CRUD 過程的控制更加精細(xì)。但推薦使用 one-way binding。

單向綁定

我們對上一篇程序代碼進(jìn)行修改,先來看看單向綁定。和上一篇比較,代碼的變化集中在 App.controller.js 。首先貼出全部代碼:

App.controller.js:

sap.ui.define([
    "sap/ui/core/mvc/Controller"
], function(Controller) {
    "use strict";

    var oModel;
    var sCurrentPath;
    var sCurrentEmp; // cureent employee
    var oEmployeeDialog;

    return Controller.extend("zui5_odata_sap_backend_crud.controller.App", {

        onInit: function() {
            oModel = this.getOwnerComponent().getModel();
            oModel.setUseBatch(false);
            this.getView().setModel(oModel);

            oEmployeeDialog = this.buildEmpDialog();
        },

        // Build employee dialog
        // If not exists, create an instance, otherwise, just get it.
        buildEmpDialog: function() {
            var oView = this.getView();

            var oEmpDialog = oView.byId("employeeDialog");
            if (!oEmpDialog) {
                oEmpDialog = sap.ui.xmlfragment(oView.getId(),
                    "zui5_odata_sap_backend_crud.view.EmployeeDialog");
                
                oView.addDependent(oEmpDialog);

                // Attach press event for CancelButton
                var oCancelButton = oView.byId("CancelButton");
                oCancelButton.attachPress(function() {
                    oEmpDialog.close();
                });
            }

            return oEmpDialog;
        },


        // onCreate event
        onCreate: function() {
            var oView = this.getView();
            oEmployeeDialog.open();
            oEmployeeDialog.setTitle("Create Employee");
            oView.byId("EmpId").setEditable(true);
            oView.byId("SaveEdit").setVisible(false);
            oView.byId("SaveCreate").setVisible(true);

            // clear
            oView.byId("EmpId").setValue("");
            oView.byId("EmpName").setValue("");
            oView.byId("EmpAddr").setValue("");

            // commit save operation
            oView.byId("SaveCreate").attachPress(function() {

                oModel.createEntry("/EmployeeCollection", {
                    properties: {
                        "Mandt": "100",
                        "EmpId": oView.byId("EmpId").getValue(),
                        "EmpName": oView.byId("EmpName").getValue(),
                        "EmpAddr": oView.byId("EmpAddr").getValue()
                    }
                });

                oModel.submitChanges();
                sap.m.MessageToast.show("Created Successfully.");

                // close dialog
                if (oEmployeeDialog) {
                    oEmployeeDialog.close();
                }
            });
        },

        onEdit: function() {
            // no employee was selected
            if (!sCurrentEmp) {
                sap.m.MessageToast.show("No Employee was selected.");
                return;
            }

            var oView = this.getView();
            oEmployeeDialog.open();

            oEmployeeDialog.setTitle("Edit Employee");
            oView.byId("EmpId").setEditable(false);
            oView.byId("SaveEdit").setVisible(true);
            oView.byId("SaveCreate").setVisible(false);

            // Attach save event
            oView.byId("SaveEdit").attachPress(function() {

                // changes
                var oChanges = {
                    "EmpName": oView.byId("EmpName").getValue(),
                    "EmpAddr": oView.byId("EmpAddr").getValue()
                };

                oModel.setProperty(sCurrentPath + "/EmpName", oChanges.EmpName);
                oModel.setProperty(sCurrentPath + "/EmpAddr", oChanges.EmpAddr);

                if (oModel.hasPendingChanges()) {
                    oModel.submitChanges();
                    sap.m.MessageToast.show("Changes were saved successfully.");
                }
                
                // close dialog
                if (oEmployeeDialog) {
                    oEmployeeDialog.close();
                }
            });
        },

        // onDelete event
        onDelete: function() {
            var that = this;

            // no employee was selected
            if (!sCurrentEmp) {
                sap.m.MessageToast.show("No Employee was selected.");
                return;
            }

            var oDeleteDialog = new sap.m.Dialog();
            oDeleteDialog.setTitle("Deletion");

            var oText = new sap.m.Label({
                text: "Are you sure to delete employee [" + sCurrentEmp + "]?"
            });
            oDeleteDialog.addContent(oText);

            oDeleteDialog.addButton(
                new sap.m.Button({
                    text: "Confirm",
                    press: function() {
                        that.deleteEmployee();
                        oDeleteDialog.close();
                    }
                })
            );

            oDeleteDialog.open();
        },

        // deletion operation
        deleteEmployee: function() {
            oModel.remove(sCurrentPath, {
                success: function() {
                    sap.m.MessageToast.show("Deletion successful.");
                },
                error: function(oError) {
                    window.console.log("Error", oError);
                }
            });
        },

        onItemPress: function(evt) {
            var oContext = evt.getSource().getBindingContext();
            sCurrentPath = oContext.getPath();
            sCurrentEmp = oContext.getProperty("EmpName");

            oEmployeeDialog.bindElement(sCurrentPath);
        }
    });
});

說明一下主要代碼的變化:

修改數(shù)據(jù)

單向綁定時,提交編輯的修改通過 setProperty() 方法將變更提交到 OData model,然后用 submitChanges() 方法將變更提交到數(shù)據(jù)源。如果想取消修改,則使用 resetChanges() 方法

var oChanges = {
    "EmpName": oView.byId("EmpName").getValue(),
    "EmpAddr": oView.byId("EmpAddr").getValue()
};

oModel.setProperty(sCurrentPath + "/EmpName", oChanges.EmpName);
oModel.setProperty(sCurrentPath + "/EmpAddr", oChanges.EmpAddr);

if (oModel.hasPendingChanges()) {
    oModel.submitChanges();
    sap.m.MessageToast.show("Changes were saved successfully.");
}

oModel.hasPendingChanges() 確保有變更才將變更提交到數(shù)據(jù)源。

新增數(shù)據(jù)

新增數(shù)據(jù),使用 ODataModel 的 createEntry() 方法,將新增的數(shù)據(jù)提交到 OData model,然后使用 submitChanges() 方法將請求隊列中提交到數(shù)據(jù)源。如果想取消新增,使用 deleteCreatedEntry() 方法。

oModel.createEntry("/EmployeeCollection", {
    properties: {
        "Mandt": "100",
        "EmpId": oView.byId("EmpId").getValue(),
        "EmpName": oView.byId("EmpName").getValue(),
        "EmpAddr": oView.byId("EmpAddr").getValue()
    }
});

oModel.submitChanges();

雙向綁定

雙向綁定,也涉及到 Edit 及 Create 時候,UI 和 Model 的數(shù)據(jù)交互。但 UI 和 Model 之間變化自動同步。

1). 在 manifest.json 文件中設(shè)置默認(rèn)的綁定模式:

"sap.ui5": {
    ...
        "models": {
            "i18n": {
                "type": "sap.ui.model.resource.ResourceModel",
                "settings": {
                    "bundleName": "zui5_odata_sap_backend_crud.i18n.i18n"
                }
            },
            "": {
                "dataSource": "mainService",
                "type": "sap.ui.model.odata.v2.ODataModel",
                "settings": {
                    "defaultBindingMode": "TwoWay",
                    "metadataUrlParams": {
                        "sap-documentation": "heading"
                    }
                }
            }
        },
        ...
    }

defaultBindingMode 設(shè)置為 TwoWay

2). Edit 不需要使用 setProperty() 方法:

oView.byId("SaveEdit").attachPress(function() {

    if (oModel.hasPendingChanges()) {
        oModel.submitChanges();
        sap.m.MessageToast.show("Changes were saved successfully.");
    }
    
    // close dialog
    if (oEmployeeDialog) {
        oEmployeeDialog.close();
    }
});

因為是雙向綁定的,UI 中如果有數(shù)據(jù)變化 oModel.hasPendingChanges() 可以自動獲得獲取,不需要 setProperty()

3). Create 的代碼:

oEmployeeDialog.unbindElement();
var oContext = oModel.createEntry("/EmployeeCollection", {
    properties: {
        "Mandt": "100"
    }
});
oEmployeeDialog.setBindingContext(oContext);

// commit save operation
oView.byId("SaveCreate").attachPress(function() {
    if (oView.byId("EmpId").getValue()) {
        oModel.submitChanges();
        sap.m.MessageToast.show("Created Successfully.");

        // close dialog
        if (oEmployeeDialog) {
            oEmployeeDialog.close();
        }
    } else {
        sap.m.MessageToast.show("Employee Id cannot be blank.");
    }
});

進(jìn)入 Dialog 的時候,首先解除與已有數(shù)據(jù)的綁定,然后使用 createEntry() 方法創(chuàng)建一個新的 context。因為 Mandt 字段在 UI 固定為 100,UI 不用管,所以放在 properties 中。設(shè)定 oEmpoyeeDialogbindingContext 后,UI 的修改就自動提交給 Model 了。

源代碼

38_zui5_odata_oneway_binding
38_zui5_odata_twoway_binding

參考資料

Binding Modes
OData V2 Model
Setting the Default Binding Mode

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

推薦閱讀更多精彩內(nèi)容