單向綁定和雙向綁定概述
所謂的單向綁定 (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è)定 oEmpoyeeDialog
的 bindingContext
后,UI 的修改就自動提交給 Model
了。
源代碼
38_zui5_odata_oneway_binding
38_zui5_odata_twoway_binding
參考資料
Binding Modes
OData V2 Model
Setting the Default Binding Mode