SAPUI5 (38) - OData Model 的单向绑定和双向绑定

单向绑定和双向绑定概述

所谓的单向绑定 (one-way binding),是指 OData model 与 UI 控件之间在数据绑定时, OData model 中数据的变化会同步反应在 UI 控件中,但 UI 控件数据的变化需要手工提交到 OData model;而双向绑定 (two-way binding),则是指 OData model 和 UI 控件的数据双向同步变化。

OData (v2) model 默认的是 one-way binding。OData model 刚开始只支持单向绑定,后来也支持 two-way binding。

使用 one-way 或者 two-way binding,可以进一步简化客户端代码的编写,同时对 CRUD 过程的控制更加精细。但推荐使用 one-way binding。

单向绑定

我们对上一篇程序代码进行修改,先来看看单向绑定。和上一篇比较,代码的变化集中在 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);
        }
    });
});

说明一下主要代码的变化:

修改数据

单向绑定时,提交编辑的修改通过 setProperty() 方法将变更提交到 OData model,然后用 submitChanges() 方法将变更提交到数据源。如果想取消修改,则使用 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() 确保有变更才将变更提交到数据源。

新增数据

新增数据,使用 ODataModel 的 createEntry() 方法,将新增的数据提交到 OData model,然后使用 submitChanges() 方法将请求队列中提交到数据源。如果想取消新增,使用 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 的数据交互。但 UI 和 Model 之间变化自动同步。

1). 在 manifest.json 文件中设置默认的绑定模式:

"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 设置为 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 中如果有数据变化 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.");
    }
});

进入 Dialog 的时候,首先解除与已有数据的绑定,然后使用 createEntry() 方法创建一个新的 context。因为 Mandt 字段在 UI 固定为 100,UI 不用管,所以放在 properties 中。设定 oEmpoyeeDialogbindingContext 后,UI 的修改就自动提交给 Model 了。

源代码

38_zui5_odata_oneway_binding
38_zui5_odata_twoway_binding

参考资料

Binding Modes
OData V2 Model
Setting the Default Binding Mode

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,324评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,303评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,192评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,555评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,569评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,566评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,927评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,583评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,827评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,590评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,669评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,365评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,941评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,928评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,159评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,880评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,399评论 2 342

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,392评论 25 707
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,580评论 18 139
  • 1、概述 Databinding 是一种框架,MVVM是一种模式,两者的概念是不一样的。我的理解DataBindi...
    Kelin阅读 76,747评论 68 521
  • 兼职创业一年半,原本“我以为”业务框架理顺后,按照既有的模式按部就班就能平稳运转下去,事与愿违,止步不前的公司业绩...
    彭砰砰阅读 286评论 2 1
  • http://www.jianshu.com/p/dcaebea291de
    SmallTwo阅读 280评论 0 0