单向绑定和双向绑定概述
所谓的单向绑定 (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
中。设定 oEmpoyeeDialog
的 bindingContext
后,UI 的修改就自动提交给 Model
了。
源代码
38_zui5_odata_oneway_binding
38_zui5_odata_twoway_binding
参考资料
Binding Modes
OData V2 Model
Setting the Default Binding Mode