上次我們介紹了屬性綁定。屬性綁定用于綁定單條數(shù)據(jù)。如果需要綁定多條數(shù)據(jù),則需要使用聚合綁定(aggregation binding),比如我們常見(jiàn)的ListBox, Combox或者表格,都是含有多條數(shù)據(jù)的。概念比較容易理解,關(guān)鍵是綁定的語(yǔ)法。如果所有行都用同樣的方法顯示數(shù)據(jù),用template方法。什么是template方法呢?就是各行的數(shù)據(jù)顯示方式應(yīng)該是固定的,像套用模板一樣。比如顯示供應(yīng)商信息,每一行的第一列是供應(yīng)商ID,第二列是供應(yīng)商名稱,都是固定的。熟悉ABAP的語(yǔ)言的讀者,可以根據(jù)ABAP內(nèi)表的line type來(lái)理解。
我們?cè)谇懊嬗?code>sap.ui.table.Table顯示供應(yīng)商數(shù)據(jù)的時(shí)候,已經(jīng)有相應(yīng)的例子,請(qǐng)參考:SAPUI5 (08) - MVC的Model和數(shù)據(jù)綁定。 Template方式綁定有以下幾種方式:
- 使用settings設(shè)置
- 使用
sap.ui.base.ManagedObject
的bindAggregation()
方法 - 使用控件的類(lèi)型化綁定方法
這三種方法本篇都會(huì)介紹。如果想各行綁定的數(shù)據(jù)有變化,或者說(shuō)是動(dòng)態(tài)的。比如在一個(gè)表格中,有一列顯示讀者對(duì)某文章的點(diǎn)擊次數(shù),當(dāng)點(diǎn)擊次數(shù)超過(guò)100時(shí),除了點(diǎn)擊次數(shù),還顯示熱門(mén)標(biāo)記,則可以使用factory function的方式來(lái)綁定。后面也會(huì)給出示例。
本次使用sap.m.Table
控件顯示數(shù)據(jù),所以先介紹下sap.m.Table
的一些語(yǔ)法要點(diǎn)。
sap.m.Table
sap.m.Table
control provides a set of sophisticated and convenience functions for responsive table design. To render the sap.m.Table properly, the order of the columns aggregation should match with the order of the items cells aggregation. Also sap.m.Table requires at least one visible sap.m.Column in columnsaggregation. For mobile devices, the recommended limit of table rows is 100 (based on 4 columns) to assure proper performance. To improve initial rendering on large tables, use the growing feature.
查看
sap.m.Table
從sap.m.ListBase
類(lèi)繼承,用于顯示包含行和列的表格式數(shù)據(jù)。表格的列可以通過(guò)columns
聚合屬性來(lái)設(shè)置,也可以使用addColumn()
方法來(lái)添加。每一列都是sap.m.Column
對(duì)象。至少包含一個(gè)可見(jiàn)列。在一定設(shè)備上不要加載太多行,以免影響性能。
sap.m.Table
的重要屬性:
-
columns
: 定義Table包含哪些列,類(lèi)型是sap.m.Column
數(shù)組。
另外,sap.m.Table
從sap.m.ListBase
繼承,所以可以直接使用sap.m.ListBase
的屬性:
-
growing
: 設(shè)置Table顯示的數(shù)據(jù)可以依據(jù)向model的請(qǐng)求增加行 -
noDataText
: 當(dāng)Table沒(méi)有數(shù)據(jù)的時(shí)候顯示的文本,類(lèi)型是string -
items
:sap.m.ListItemBase
數(shù)組,sap.m.ListItemBase類(lèi)定義了列表項(xiàng)(list item)的基本特征。
sap.m.ColumnListItem
使用template方法顯示數(shù)據(jù),每一行的template常用sap.m.ColumnListItem
,所以接下來(lái)介紹sap.m.ColumnListItem
的知識(shí)點(diǎn):
sap.m.ColumnListItem
can be used with the cells aggregation to create rows for thesap.m.Table
control. The columns aggregation of thesap.m.Table
should match with the cells aggregation.
查看
sap.m.ColumnListItem
用于創(chuàng)建sap.m.Table
的行,行中包含的cells需要與sap.m.Table
的Columns匹配,順序一致。
本次將用到以下屬性:
-
vAlign
, 行的垂直對(duì)齊:- sap.ui.core.VerticalAlign.Bottom,底部對(duì)齊
- sap.ui.core.VerticalAlign.Inherit,從父控件繼承
- [sap.ui.core.VerticalAlign.Middle, 居中對(duì)齊
- sap.ui.core.VerticalAlign.Top,頂部對(duì)齊
cells
: 行包含的cells,每一個(gè)cell都是sap.ui.core.Control
對(duì)象,從而開(kāi)發(fā)人員可以根據(jù)需要選擇合適的控件,靈活度很高。
聚合綁定示例
和之前一樣,通過(guò)例子來(lái)加強(qiáng)理解。今天要實(shí)現(xiàn)的業(yè)務(wù)場(chǎng)景是在頁(yè)面中顯示一個(gè)文章列表,這些文章的閱讀次數(shù),我們的要顯示的界面如下:
為了便于理解,先給出application area的完整代碼:
/**
* Aggregation binding
* Demo written by Stone Wang
*/
// application data
var oAppData = [
{ articleName: "SAP成本計(jì)算流程", type: "Locked", hits: 1048 },
{ articleName: "SAP物料價(jià)格修改", type: "Draft", hits: 58 },
{ articleName: "2017年SAP技術(shù)趨勢(shì)", type: "Unsaved", hits: 320},
{ articleName: "《人類(lèi)簡(jiǎn)史》讀后感", type: "Flagged", hits: 90 },
{ articleName: "《Core Java》第十版出版", type: "Favorite" , hits: 66}
];
var oModel = new sap.ui.model.json.JSONModel();
oModel.setData({ modelData: oAppData });
sap.ui.getCore().setModel(oModel);
// 定義一個(gè)包含包含3列的數(shù)組
var aColumns = [
new sap.m.Column({
header : new sap.m.Label({text : "文章"})
}),
new sap.m.Column({
header : new sap.m.Label({text : "標(biāo)記"})
}),
new sap.m.Column({
header : new sap.m.Label({text : "標(biāo)記"})
})
]
// 定義template, 每行包含3個(gè)cell
var oColumnListItem = new sap.m.ColumnListItem({
vAlign: "Middle",
cells: [
new sap.m.Text({text: "{articleName}"}),
new sap.m.ObjectMarker({type: "{type}"}),
new sap.m.ObjectMarker({
type: "{type}",
active: true,
press: function(oEvent){
sap.m.MessageToast.show(oEvent.getParameter("type") + " pressed");
}
})
]
});
// Table control
var oTable = new sap.m.Table({
columns : aColumns,
items: {path: "/modelData", template: oColumnListItem}
});
var oTablePanel = new sap.m.Panel({
headerText: "文章列表",
content: oTable
});
var oStandalonePanel = new sap.m.Panel("standalone-panel", {
headerText: "圖例:",
content: [
new sap.m.ObjectMarker({type: sap.m.ObjectMarkerType.Locked}),
new sap.m.ObjectMarker({type: sap.m.ObjectMarkerType.Flagged}),
new sap.m.ObjectMarker({type: sap.m.ObjectMarkerType.Favorite}),
new sap.m.ObjectMarker({type: sap.m.ObjectMarkerType.Draft}),
new sap.m.ObjectMarker({type: sap.m.ObjectMarkerType.Unsaved})
]
});
var oApp = new sap.m.App({ initialPage: "page" });
var oPage = new sap.m.Page("page", {
title:"Aggregation binding demo",
content: [oTablePanel, oStandalonePanel]
});
oApp.addPage(oPage).placeAt("content");
我們說(shuō)明最主要的部分。因?yàn)門(mén)able包含三列,所以我們先定義一個(gè)包含3列的數(shù)組,每一列都是sap.m.Column
對(duì)象。
// 定義一個(gè)包含包含3列的數(shù)組
var aColumns = [
new sap.m.Column({
header : new sap.m.Label({text : "文章"})
}),
new sap.m.Column({
header : new sap.m.Label({text : "標(biāo)記"})
}),
new sap.m.Column({
header : new sap.m.Label({text : "標(biāo)記"})
})
]
剛才說(shuō)過(guò),對(duì)于每一行來(lái)說(shuō),都是一個(gè)template,我們使用ColumnListItem來(lái)代表template,每一行包含三個(gè)單元格,使用cells屬性表示。每一個(gè)cell都是Control對(duì)象,我們使用sap.m.Text
顯示第一個(gè)單元格,綁定到articleName,使用sap.m.ObjectMarker
顯示第二個(gè)單元格和第三個(gè)單元格,第三個(gè)單元格與event handler綁定:
// 定義template, 每行包含3個(gè)cell
var oColumnListItem = new sap.m.ColumnListItem({
vAlign: "Middle",
cells: [
new sap.m.Text({text: "{articleName}"}),
new sap.m.ObjectMarker({type: "{type}"}),
new sap.m.ObjectMarker({
type: "{type}",
active: true,
press: function(oEvent){
sap.m.MessageToast.show(oEvent.getParameter("type") + " pressed");
}
})
]
});
最后定義sap.m.Table
對(duì)象,使用columns
聚合屬性和items
聚合屬性,items屬性實(shí)現(xiàn)的就是聚合綁定,當(dāng)然,需要ColumnListItem
的支持。
// Table control
var oTable = new sap.m.Table({
columns : aColumns,
items: {path: "/modelData", template: oColumnListItem}
});
對(duì)Table的綁定,我們也可以使用bingItems方法來(lái)實(shí)現(xiàn):
var oTable = new sap.m.Table({
columns: aColumns
});
oTable.bindItems("/modelData", oColumnListItem);
或者:
var oTable = new sap.m.Table({
columns: aColumns
});
oTable.bindAggregation("items", "/modelData", oColumnListItem);
使用工廠函數(shù)實(shí)現(xiàn)聚合綁定
對(duì)上面的例子進(jìn)行重構(gòu),假設(shè)我們想顯示這些文章的閱讀次數(shù),并且當(dāng)閱讀次數(shù)超過(guò)100時(shí),就在閱讀次數(shù)下面加一個(gè)”熱門(mén)”字眼來(lái)標(biāo)識(shí)。也就是不同的單元格在顯示的時(shí)候是動(dòng)態(tài)的。對(duì)這種動(dòng)態(tài)的數(shù)據(jù)顯示,就需要用factory function。如何做呢?我們使用sap.m.Table
的bindAggregation()
方法,參數(shù)3使用匿名函數(shù),這個(gè)函數(shù)就是factory function:
// 使用Factory function實(shí)現(xiàn)動(dòng)態(tài)的數(shù)據(jù)顯示
oTable.bindAggregation("items", "/modelData", function(sId, oContext){
var oColumnListItem = new sap.m.ColumnListItem(sId, {vAlign: "Middle"});
oColumnListItem.addCell(new sap.m.Text({text: "{articleName}"}));
oColumnListItem.addCell(new sap.m.ObjectMarker({type: "{type}"}));
var oHits = oContext.getProperty("hits");
if (oHits >= 100) {
oColumnListItem.addCell(
new sap.ui.layout.VerticalLayout({
content: [
new sap.m.Text({text: "{hits}"}),
new sap.m.ObjectStatus({text:"熱門(mén)", state:"Success"})
]
})
)
} else {
oColumnListItem.addCell(
new sap.m.Text({text: "{hits}"})
)
}
return oColumnListItem;
});
注意Facotry 函數(shù)的參數(shù)必須是sId和oContext。如果點(diǎn)擊次數(shù)大于或等于100,則cell中包括一個(gè)sap.m.Text
和一個(gè)sap.m.ObjectStatus
對(duì)象,垂直布局。如果點(diǎn)擊次數(shù)小于100,則只有一個(gè)sap.m.Text
來(lái)顯示。函數(shù)最后需要使用return語(yǔ)句返回sap.m.ColumnListItem
對(duì)象。
貼上完整代碼:
/**
* Aggregation binding using factory function
* Demo written by Stone Wang
*/
// application data
var oAppData = [
{ articleName: "SAP成本計(jì)算流程", type: "Locked", hits: 1048 },
{ articleName: "SAP物料價(jià)格修改", type: "Draft", hits: 58 },
{ articleName: "2017年SAP技術(shù)方向", type: "Unsaved", hits: 320},
{ articleName: "《人類(lèi)簡(jiǎn)史》讀后感", type: "Flagged", hits: 90 },
{ articleName: "《Core Java》第十版出版", type: "Favorite" , hits: 66}
];
var oModel = new sap.ui.model.json.JSONModel();
oModel.setData({ modelData: oAppData });
sap.ui.getCore().setModel(oModel);
// 定義Column數(shù)組,包含3列
var aColumns = [
new sap.m.Column({
header : new sap.m.Label({text : "文章"})
}),
new sap.m.Column({
header : new sap.m.Label({text : "標(biāo)記"})
}),
new sap.m.Column({
header : new sap.m.Label({text: "閱讀次數(shù)"})
})
]
// Table control
var oTable = new sap.m.Table({
columns : aColumns
});
// 使用Factory function實(shí)現(xiàn)動(dòng)態(tài)的數(shù)據(jù)顯示
oTable.bindAggregation("items", "/modelData", function(sId, oContext){
var oColumnListItem = new sap.m.ColumnListItem(sId, {vAlign: "Middle"});
oColumnListItem.addCell(new sap.m.Text({text: "{articleName}"}));
oColumnListItem.addCell(new sap.m.ObjectMarker({type: "{type}"}));
var oHits = oContext.getProperty("hits");
if (oHits >= 100) {
oColumnListItem.addCell(
new sap.ui.layout.VerticalLayout({
content: [
new sap.m.Text({text: "{hits}"}),
new sap.m.ObjectStatus({text:"熱門(mén)", state:"Success"})
]
})
)
} else {
oColumnListItem.addCell(
new sap.m.Text({text: "{hits}"})
)
}
return oColumnListItem;
});
var oTablePanel = new sap.m.Panel({
headerText: "文章列表",
content: oTable
});
var oStandalonePanel = new sap.m.Panel("standalone-panel", {
headerText: "圖例:",
content: [
new sap.m.ObjectMarker({type: sap.m.ObjectMarkerType.Locked}),
new sap.m.ObjectMarker({type: sap.m.ObjectMarkerType.Flagged}),
new sap.m.ObjectMarker({type: sap.m.ObjectMarkerType.Favorite}),
new sap.m.ObjectMarker({type: sap.m.ObjectMarkerType.Draft}),
new sap.m.ObjectMarker({type: sap.m.ObjectMarkerType.Unsaved})
]
});
var oApp = new sap.m.App({ initialPage: "page" });
var oPage = new sap.m.Page("page", {
title:"Aggregation binding demo",
content: [oTablePanel, oStandalonePanel]
});
oApp.addPage(oPage).placeAt("content");
頁(yè)面顯示效果如下:
用xmlview實(shí)現(xiàn)factory方法的聚合綁定
xmlview是聲明式的,factory方式的聚合綁定卻是為了實(shí)現(xiàn)動(dòng)態(tài)的顯示。那么,如何在xmlview中實(shí)現(xiàn)動(dòng)態(tài)呢?要點(diǎn)如下:
1)xmlview中對(duì)需要?jiǎng)討B(tài)顯示的部分不作聲明
2)在controller中定義factory function,實(shí)現(xiàn)控件的綁定和動(dòng)態(tài)加載。
仍然用剛才的例子進(jìn)行重構(gòu),項(xiàng)目文件和路徑如下:
將數(shù)據(jù)放在json文件articles.json中,內(nèi)容:
[
{ "articleName": "SAP成本計(jì)算流程", "type": "Locked", "hits": 1048 },
{ "articleName": "SAP物料價(jià)格修改", "type": "Draft", "hits": 58 },
{ "articleName": "2017年SAP技術(shù)方向", "type": "Unsaved", "hits": 320},
{ "articleName": "《人類(lèi)簡(jiǎn)史》讀后感", "type": "Flagged", "hits": 90 },
{ "articleName": "《Core Java》第十版出版", "type": "Favorite" , "hits": 66}
]
index.html只有oApp
和oView,
oApp`放在DIV中。
<!DOCTYPE HTML>
<html>
<head>
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta http-equiv='Content-Type' content='text/html;charset=UTF-8'/>
<script src="resources/sap-ui-core.js"
id="sap-ui-bootstrap"
data-sap-ui-libs="sap.m, sap.ui.layout"
data-sap-ui-resourceroots='{"bindingtest": "./binding_test"}'
data-sap-ui-theme="sap_bluecrystal">
</script>
<script>
var oApp = new sap.m.App({initialPage: "mainpage"});
var oView = sap.ui.xmlview({
id: "mainpage",
viewName: "bindingtest.view.aggregation_binding"
});
oApp.addPage(oView);
oApp.placeAt("content");
</script>
</head>
<body class="sapUiBody" role="application">
<div id="content"></div>
</body>
</html>
aggregation_binding.view.xml:
<core:View xmlns:core="sap.ui.core"
xmlns:mvc="sap.ui.core.mvc"
xmlns="sap.m"
controllerName="bindingtest.controller.aggregation_binding"
xmlns:html="http://www.w3.org/1999/xhtml">
<Panel class="sapUiSmallMargin" headerText="文章列表">
<content>
<Table id="table" width="auto" class="sapUiSmallMargin"
noDataText="no data">
<columns>
<Column><header><Label text="文章" /></header></Column>
<Column><header><Label text="標(biāo)記" /></header></Column>
<Column hAlign="Right"><header><Label text="閱讀次數(shù)" /></header></Column>
</columns>
</Table>
</content>
</Panel>
<Panel class="sapUiSmallMargin" headerText="圖例">
<content>
<ObjectMarker class="sapUiSmallMargin" type="Locked" />
<ObjectMarker class="sapUiSmallMargin" type="Flagged" />
<ObjectMarker class="sapUiSmallMargin" type="Favorite" />
<ObjectMarker class="sapUiSmallMargin" type="Draft" />
<ObjectMarker class="sapUiSmallMargin" type="Unsaved" />
</content>
</Panel>
</core:View>
注意sap.m.Table只聲明了columns,沒(méi)有聲明items。items在代碼中實(shí)現(xiàn)。
aggregation_binding.controller.js:
sap.ui.define(["sap/ui/core/mvc/Controller"],
function(Controller){
"use strict";
// controller name
return Controller.extend("bindingtest.controller.aggregation_binding", {
//-------------------------------
// initialization
//-------------------------------
onInit: function() {
// binding view with model
var oModel = sap.ui.model.json.JSONModel();
oModel.loadData('binding_test/model/articles.json');
this.getView().setModel(oModel);
// Table object add items
var oTable = this.getView().byId("table");
oTable.bindItems({path: '/', factory: this.createCellsFactory});
},
//------------------------------------------------
// Factory function to add cells for table
//------------------------------------------------
createCellsFactory: function(sId, oContext) {
var oColumnListItem = new sap.m.ColumnListItem(sId, {vAlign: "Middle"});
// first two cells are not dynamic
oColumnListItem.addCell(new sap.m.Text({text: "{articleName}"}));
oColumnListItem.addCell(new sap.m.ObjectMarker({type: "{type}"}));
// third cell is dynamic
var oHits = oContext.getProperty("hits");
if (oHits >= 100) {
oColumnListItem.addCell(
new sap.ui.layout.VerticalLayout({
content: [
new sap.m.Text({text: "{hits}"}),
new sap.m.ObjectStatus({text:"熱門(mén)", state:"Success"})
]
})
)
} else {
oColumnListItem.addCell(
new sap.m.Text({text: "{hits}"})
)
}
return oColumnListItem;
} // end of createCellsFactory()
});
}
);
在onInit()中調(diào)用oTable的bindItems方法,bindItems方法包含factory方法: oTable.bindItems({path: '/', factory: this.createCellsFactory});