Aggregation Binding
创建Invoices.json文件
{
"Invoices": [
{
"ProductName": "Pineapple",
"Quantity": 21,
"ExtendedPrice": 87.2000,
"ShipperName": "Fun Inc.",
"ShippedDate": "2015-04-01T00:00:00",
"Status": "A"
},
......
]
}
将invoice添加到manifest.json的models中
"invoice": {
"type": "sap.ui.model.json.JSONModel",
"uri": "Invoices.json"
}
新建InvoiceList.view.xml,并在app view中通过<mvc:XMLView viewName="sap.ui.demo.wt.view.InvoiceList"/>
绑定,添加List,items为invoice Model下/Invoices
,items的title为Quantity x ProductName
<mvc:View
xmlns="sap.m"
xmlns:mvc="sap.ui.core.mvc">
<List
headerText="{i18n>invoiceListTitle}"
class="sapUiResponsiveMargin"
width="auto"
items="{invoice>/Invoices}" >
<items>
<ObjectListItem
title="{invoice>Quantity} x {invoice>ProductName}"/>
</items>
</List>
</mvc:View>
Data Types
指定invoicelist的controller,在controller的onInit事件中,添加JSONModel{currency: "EUR"}
,并绑定到Model this.getView().setModel(oViewModel, "view");
。在ObjectListItem中添加number与numberUnit属性,number使用Calculated Fields语法,type为sap.ui.model.type.Currency
金额类型,需要两个parts,分别为金额和单位,在number属性中我们不显示单位,因此设置formatOptions: {showMeasure: false}
Expression Binding
在ObjectListItem中添加numberState属性,将其定义为三元表达式numberState="{= ${invoice>ExtendedPrice} > 50 ? 'Error' : 'Success' }"/>
,表达式可以用{=expression}
来表示,式中${invoice>ExtendedPrice}
取出ExtendedPrice的值。
Custom Formatters
定义formatter.js模块,返回status text
sap.ui.define([], function () {
"use strict";
return {
statusText: function (sStatus) {
var resourceBundle = this.getView().getModel("i18n").getResourceBundle();
switch (sStatus) {
case "A":
return resourceBundle.getText("invoiceStatusA");
case "B":
return resourceBundle.getText("invoiceStatusB");
case "C":
return resourceBundle.getText("invoiceStatusC");
default:
return sStatus;
}
}
};
});
在InvoiceList controller中,引入formatter.js模块依赖,定义formatter事件formatter: formatter,
在ObjectListItem中添加<firstStatus>标签,在ObjectStatus中定义formatter为'.formatter.statusText'
,表示当前controller下的formatter,并调用statusText
<ObjectStatus text="{
path: 'invoice>Status',
formatter: '.formatter.statusText'
}"/>
Filtering
给list一个idid="invoiceList"
,添加headertoolbar,加入搜索框,添加onFilterInvoices事件。
<headerToolbar>
<Toolbar>
<Title text="{i18n>invoiceListTitle}"/>
<ToolbarSpacer/>
<SearchField width="50%" search="onFilterInvoices" selectOnFocus="false"/>
</Toolbar>
</headerToolbar>
在controller中添加Filter FilterOperator依赖,实现onFilterInvoices。SearchField触发事件后,将输入的值添加到query参数下,可以通过 oEvent.getParameter("query")得到输入的Search字段,创建一个Filter对象并将它push到aFilter数组中,Filter参数为path: ProductName,Operator:Contains,Value:sQuery。获取invoiceList的ListBinding对象,调用filter方法
onFilterInvoices : function (oEvent) {
// build filter array
var aFilter = [];
var sQuery = oEvent.getParameter("query");
if (sQuery) {
aFilter.push(new Filter("ProductName", FilterOperator.Contains, sQuery));
}
// filter binding
var oList = this.getView().byId("invoiceList");
var oBinding = oList.getBinding("items");
oBinding.filter(aFilter);
}
Sorting and Grouping
在items中定义sorter及group
items="{
path : 'invoice>/Invoices',
sorter : {
path : 'ShipperName',
group : true
}
Remote OData Service
跨域请求配置
配置manifest.json,
在"sap.app"下添加dataSources
"dataSources": {
"invoiceRemote": {
"uri": "https://services.odata.org/V2/Northwind/Northwind.svc/",
"type": "OData",
"settings": {
"odataVersion": "2.0"
}
}
}
invoice Model改成使用dataSource
"invoice": {
"dataSource": "invoiceRemote"
}
在Eclipse中测试时,使用本地代理
将uri改为proxy/V2/Northwind/Northwind.svc/
在web.xml中,SimpleProxyServlet下面添加如下配置,eclipse的Network connection需为Native或Direct
<context-param>
<param-name>com.sap.ui5.proxy.REMOTE_LOCATION</param-name>
<param-value>http://services.odata.org</param-value>
</context-param>
Mock Server Configuration
创建mockServer.html,在attachInit中,需要引入依赖,可以用sap.ui.require
sap.ui.require([
"sap/ui/demo/wt/localService/mockserver",
"sap/m/Shell",
"sap/ui/core/ComponentContainer"
], function (mockserver, Shell, ComponentContainer) {
mockserver.init();
new Shell({
app : new ComponentContainer({
height: "100%",
name: "sap.ui.demo.wt"
})
}).placeAt("content");
});
其中,mockserver.js模块的定义如下,其中,创建MockServer的rootUri需要匹配上manifest.json文件中的URI,MockServer.config进行全局配置。simulate方法传入Metadata的path及Mockdata的path,Metadata参考odata Metadata定义,Mockdata为json文件,通过start方法启动。
sap.ui.define([
"sap/ui/core/util/MockServer"
], function (MockServer) {
"use strict";
return {
init: function () {
// create
var oMockServer = new MockServer({
rootUri: "/destinations/northwind/V2/Northwind/Northwind.svc/"
});
var oUriParameters = jQuery.sap.getUriParameters();
// configure
MockServer.config({
autoRespond: true,
autoRespondAfter: oUriParameters.get("serverDelay") || 1000
});
// simulate
var sPath = jQuery.sap.getModulePath("sap.ui.demo.wt.localService");
oMockServer.simulate(sPath + "/metadata.xml", sPath + "/mockdata");
// start
oMockServer.start();
}
};
});
Unit Test
测试前面创建的formatter.js模块,创建/test/unit/model/formatter.js,QUnit.module创建Module Formatting functions
,beforeEach与afterEach事件中分别创建和销毁ResourceModel。
QUnit.module("Formatting functions", {
beforeEach: function () {
this._oResourceModel = new ResourceModel({
bundleUrl : jQuery.sap.getModulePath("sap.ui.demo.wt", "/i18n/i18n.properties")
});
},
afterEach: function () {
this._oResourceModel.destroy();
}
});
QUnit.test进行测试,测试名为Should return the translated texts
,方法中,由于formatter中代码var resourceBundle = this.getView().getModel("i18n").getResourceBundle();
我们不需要controller,view,model等功能,我们用SinonJS的stub方法来移除依赖,controller的getView方法返回view,我们这里this.stub()模拟controller,返回viewStub,oViewStub中,getModel返回ResourceModel,这里this.stub()模拟view,withArgs("i18n")模拟参数。然后将formatter.statusText绑定模拟的controller,此时代码中的this将指向ControllerStub。
QUnit.test("Should return the translated texts", function (assert) {
// Arrange
var oViewStub = {
getModel: this.stub().withArgs("i18n").returns(this._oResourceModel)
};
var oControllerStub = {
getView: this.stub().returns(oViewStub)
};
// System under test
var fnIsolatedFormatter = formatter.statusText.bind(oControllerStub);
// Assert
assert.strictEqual(fnIsolatedFormatter("A"), "New", "The long text for status A is correct");
});
Integration Test with OPA
创建/test/integration/navigationJourney.js,引入opaQunit依赖,创建Navigation Module,创建opaTest,Given对象中调用准备function,本例中创建一个frame,When对象中调用配置的action,Then对象中进行判断。
sap.ui.require([
"sap/ui/test/opaQunit"
], function () {
"use strict";
QUnit.module("Navigation");
opaTest("Should open the hello dialog", function (Given, When, Then) {
Given.iStartMyAppInAFrame(jQuery.sap.getResourcePath("sap/ui/demo/app/test", ".html"));
When.onTheAppPage.iPressTheSayHelloWithDialogButton();
Then.onTheAppPage.iShouldSeeTheHelloDialog().
and.iTeardownMyAppFrame();
});
});
创建/test/integration/pages/App.js文件,createPageObjects创建Page对象onTheAppPage,添加actions与assertions,通过this.waitFor返回Promise对象,如下代码会寻找Button对象数组,aButtons[0]取第一个Button,.$()创建Jquery对象,并触发tap事件
return this.waitFor({
controlType: "sap.m.Button",
success: function (aButtons) {
aButtons[0].$().trigger("tap");
},
errorMessage: "Did not find the helloDialogButton button on the app page"
});
Debug tool
在view中,把list中number改为错误的'invoice>ExTendedPrice'
,此时打开页面,Number没有值。
CTRL + ALT + SHIFT + S 打开SAPUI5 诊断工具,在control tree中,找到listitem,打开Binding Infos,可以看到number标记为invalid
有时候Debug不是那么容易,SAPUI5文件被优化成最小化版本,要进行深入的Debug源文件时,可以 CTRL + ALT + SHIFT + P 打开窗口,将Use Debug Sources 打上勾,再通过浏览器加载时,在F12开发者工具Network下有很多-dbg后缀的文件,即为未经压缩的源文件