SAPUI5 (13) - 數據綁定之聚合綁定(aggregation binding)

上次我們介紹了屬性綁定。屬性綁定用于綁定單條數據。如果需要綁定多條數據,則需要使用聚合綁定(aggregation binding),比如我們常見的ListBox, Combox或者表格,都是含有多條數據的。概念比較容易理解,關鍵是綁定的語法。如果所有行都用同樣的方法顯示數據,用template方法。什么是template方法呢?就是各行的數據顯示方式應該是固定的,像套用模板一樣。比如顯示供應商信息,每一行的第一列是供應商ID,第二列是供應商名稱,都是固定的。熟悉ABAP的語言的讀者,可以根據ABAP內表的line type來理解。

我們在前面用sap.ui.table.Table顯示供應商數據的時候,已經有相應的例子,請參考:SAPUI5 (08) - MVC的Model和數據綁定。 Template方式綁定有以下幾種方式:

  1. 使用settings設置
  2. 使用sap.ui.base.ManagedObjectbindAggregation()方法
  3. 使用控件的類型化綁定方法

這三種方法本篇都會介紹。如果想各行綁定的數據有變化,或者說是動態的。比如在一個表格中,有一列顯示讀者對某文章的點擊次數,當點擊次數超過100時,除了點擊次數,還顯示熱門標記,則可以使用factory function的方式來綁定。后面也會給出示例。

本次使用sap.m.Table控件顯示數據,所以先介紹下sap.m.Table的一些語法要點。

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.Tablesap.m.ListBase類繼承,用于顯示包含行和列的表格式數據。表格的列可以通過columns聚合屬性來設置,也可以使用addColumn()方法來添加。每一列都是sap.m.Column對象。至少包含一個可見列。在一定設備上不要加載太多行,以免影響性能。

sap.m.Table的重要屬性:

  • columns: 定義Table包含哪些列,類型是sap.m.Column數組。

另外,sap.m.Tablesap.m.ListBase繼承,所以可以直接使用sap.m.ListBase的屬性:

  • growing: 設置Table顯示的數據可以依據向model的請求增加行
  • noDataText: 當Table沒有數據的時候顯示的文本,類型是string
  • items: sap.m.ListItemBase數組,sap.m.ListItemBase類定義了列表項(list item)的基本特征。

sap.m.ColumnListItem

使用template方法顯示數據,每一行的template常用sap.m.ColumnListItem,所以接下來介紹sap.m.ColumnListItem的知識點:

sap.m.ColumnListItem can be used with the cells aggregation to create rows for the sap.m.Table control. The columns aggregation of the sap.m.Table should match with the cells aggregation.
查看

sap.m.ColumnListItem用于創建sap.m.Table的行,行中包含的cells需要與sap.m.Table的Columns匹配,順序一致。

本次將用到以下屬性:

  • vAlign, 行的垂直對齊:

    • sap.ui.core.VerticalAlign.Bottom,底部對齊
    • sap.ui.core.VerticalAlign.Inherit,從父控件繼承
    • [sap.ui.core.VerticalAlign.Middle, 居中對齊
    • sap.ui.core.VerticalAlign.Top,頂部對齊
  • cells: 行包含的cells,每一個cell都是sap.ui.core.Control對象,從而開發人員可以根據需要選擇合適的控件,靈活度很高。

聚合綁定示例

和之前一樣,通過例子來加強理解。今天要實現的業務場景是在頁面中顯示一個文章列表,這些文章的閱讀次數,我們的要顯示的界面如下:

為了便于理解,先給出application area的完整代碼:

/**
 * Aggregation binding
 * Demo written by Stone Wang
 */

// application data
var oAppData = [
    { articleName: "SAP成本計算流程", type: "Locked", hits: 1048 },
    { articleName: "SAP物料價格修改", type: "Draft", hits: 58 },
    { articleName: "2017年SAP技術趨勢", type: "Unsaved", hits: 320},
    { articleName: "《人類簡史》讀后感", 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);

// 定義一個包含包含3列的數組
var aColumns = [
    new sap.m.Column({
        header : new sap.m.Label({text : "文章"})
    }),     
    new sap.m.Column({
        header : new sap.m.Label({text : "標記"})
    }),
    new sap.m.Column({
        header : new sap.m.Label({text : "標記"})
    })
]

// 定義template, 每行包含3個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");

我們說明最主要的部分。因為Table包含三列,所以我們先定義一個包含3列的數組,每一列都是sap.m.Column對象。

// 定義一個包含包含3列的數組
var aColumns = [
    new sap.m.Column({
        header : new sap.m.Label({text : "文章"})
    }),     
    new sap.m.Column({
        header : new sap.m.Label({text : "標記"})
    }),
    new sap.m.Column({
        header : new sap.m.Label({text : "標記"})
    })
]

剛才說過,對于每一行來說,都是一個template,我們使用ColumnListItem來代表template,每一行包含三個單元格,使用cells屬性表示。每一個cell都是Control對象,我們使用sap.m.Text顯示第一個單元格,綁定到articleName,使用sap.m.ObjectMarker顯示第二個單元格和第三個單元格,第三個單元格與event handler綁定:

// 定義template, 每行包含3個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對象,使用columns聚合屬性和items聚合屬性,items屬性實現的就是聚合綁定,當然,需要ColumnListItem的支持。

// Table control
var oTable = new sap.m.Table({
    columns : aColumns,
    items: {path: "/modelData", template: oColumnListItem}
});

對Table的綁定,我們也可以使用bingItems方法來實現:

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);

使用工廠函數實現聚合綁定

對上面的例子進行重構,假設我們想顯示這些文章的閱讀次數,并且當閱讀次數超過100時,就在閱讀次數下面加一個”熱門”字眼來標識。也就是不同的單元格在顯示的時候是動態的。對這種動態的數據顯示,就需要用factory function。如何做呢?我們使用sap.m.TablebindAggregation()方法,參數3使用匿名函數,這個函數就是factory function:

// 使用Factory function實現動態的數據顯示
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:"熱門", state:"Success"})
                ]
            })
        )
    } else {
        oColumnListItem.addCell(
            new sap.m.Text({text: "{hits}"})
        )       
    }
    
    return oColumnListItem;
});

注意Facotry 函數的參數必須是sId和oContext。如果點擊次數大于或等于100,則cell中包括一個sap.m.Text和一個sap.m.ObjectStatus對象,垂直布局。如果點擊次數小于100,則只有一個sap.m.Text來顯示。函數最后需要使用return語句返回sap.m.ColumnListItem對象。

貼上完整代碼:

/**
 * Aggregation binding using factory function
 * Demo written by Stone Wang
 */

// application data
var oAppData = [
    { articleName: "SAP成本計算流程", type: "Locked", hits: 1048 },
    { articleName: "SAP物料價格修改", type: "Draft", hits: 58 },
    { articleName: "2017年SAP技術方向", type: "Unsaved", hits: 320},
    { articleName: "《人類簡史》讀后感", 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數組,包含3列
var aColumns = [
    new sap.m.Column({
        header : new sap.m.Label({text : "文章"})
    }),     
    new sap.m.Column({
        header : new sap.m.Label({text : "標記"})
    }),
    new sap.m.Column({
        header : new sap.m.Label({text: "閱讀次數"})
    })
    ]


// Table control
var oTable = new sap.m.Table({
    columns : aColumns  
});


// 使用Factory function實現動態的數據顯示
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:"熱門", 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");

頁面顯示效果如下:

用xmlview實現factory方法的聚合綁定

xmlview是聲明式的,factory方式的聚合綁定卻是為了實現動態的顯示。那么,如何在xmlview中實現動態呢?要點如下:
1)xmlview中對需要動態顯示的部分不作聲明
2)在controller中定義factory function,實現控件的綁定和動態加載。

仍然用剛才的例子進行重構,項目文件和路徑如下:

將數據放在json文件articles.json中,內容:

[
    { "articleName": "SAP成本計算流程", "type": "Locked", "hits": 1048 },
    { "articleName": "SAP物料價格修改", "type": "Draft", "hits": 58 },
    { "articleName": "2017年SAP技術方向", "type": "Unsaved", "hits": 320},
    { "articleName": "《人類簡史》讀后感", "type": "Flagged", "hits": 90 },
    { "articleName": "《Core Java》第十版出版", "type": "Favorite" , "hits": 66}
]

index.html只有oAppoView,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="標記" /></header></Column>
                    <Column hAlign="Right"><header><Label text="閱讀次數" /></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,沒有聲明items。items在代碼中實現。

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:"熱門", state:"Success"})
                            ]
                        })
                    )
                } else {
                    oColumnListItem.addCell(
                        new sap.m.Text({text: "{hits}"})
                    )       
                }
        
                return oColumnListItem;
            } // end of createCellsFactory()            
            
        });
    }
);

在onInit()中調用oTable的bindItems方法,bindItems方法包含factory方法: oTable.bindItems({path: '/', factory: this.createCellsFactory});

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

推薦閱讀更多精彩內容