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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念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

推荐阅读更多精彩内容