课程回顾
上一课中,利用Visual studio Code 新建、并运行了一个Demo工程。可以实现对项目的启动,启动后进入一个List清单。
那么本次课程的目前就是在上一节Demo的基础上,从零开始新建一个完整的页面。实现从首页清单,选择行后,鼠标点击,进入下一个页面。
准备工作
在开始之前,把上一节代码中的页面的显示的清单抽取成一个单独的json文件,这样controller层的代码就显得没有那么臃肿。但是标准版的做法其实是需要把清单数据,直接放到JSONModel对象里面。
这里演示下View层的数据如何从指定文件夹取。
1 首先把product.json文件,放在webapp文件夹下
2 在manifast.json文件内model属性配置如下:
"models": {
"i18n": {
"type": "sap.ui.model.resource.ResourceModel",
"settings": {
"bundleName": "project1.i18n.i18n"
}
},
"products": {
"type": "sap.ui.model.json.JSONModel",
"uri": "products.json"
},
主要是为了引入products.json文件。
然后修改View1的xml和controller如下:
<mvc:View controllerName="project1.controller.View1"
xmlns:mvc="sap.ui.core.mvc" displayBlock="true"
xmlns="sap.m">
<Page id="page" title="{i18n>title}">
<content>
<List
items="{products>/ProductCollection}"
headerText="Products">
<ObjectListItem
title="{products>Name}"
type="Active"
press="onListItemPress"
number="{
parts:[{path:'products>Price'},{path:'products>CurrencyCode'}],
type: 'sap.ui.model.type.Currency',
formatOptions: {showMeasure: false}
}"
numberUnit="{products>CurrencyCode}">
<firstStatus>
<ObjectStatus
text="{products>Status}"
state="{
path: 'products>Status',
formatter: 'project1.controller.Formatter.status'
}" />
</firstStatus>
<ObjectAttribute text="{products>WeightMeasure} {products>WeightUnit}" />
<ObjectAttribute text="{products>Width} x {products>Depth} x {products>Height} {products>DimUnit}" />
</ObjectListItem>
</List>
</content>
</Page>
</mvc:View>
主要的区别是所有的属性前面都加入了 products>。
View1.controller.js修改后:
sap.ui.define([
"sap/ui/core/mvc/Controller",
'sap/m/MessageToast',
'./Formatter',
'sap/ui/model/json/JSONModel'
],
/**
* @param {typeof sap.ui.core.mvc.Controller} Controller
*/
function (Controller, MessageToast, Formatter, JSONModel) {
"use strict";
return Controller.extend("project1.controller.View1", {
onInit: function () {
},
onListItemPress: function (oEvent) {
MessageToast.show("Pressed : " + oEvent.getSource().getTitle());
}
});
});
新建详情页面
首先要知道一个前提,Fiori使用了经典的MVC分层理论,即外面前面见过的view.xml视图层,controller.js业务逻辑层,和刚才我们配置的model层:products.json。
相当于把原来一个页面的业务进行了解耦分层,从结构层面来说,功能划分比较清晰。
所以新建页面我们也需要分别新建一整套MVC结构,由于这是一个可以直接用于生产的Demo项目,还要在manifest.json文件配置路由信息。
新建View
在view文件夹下新建文件View1Detail.view.xml:
<mvc:View
xmlns="sap.m"
xmlns:core="sap.ui.core"
xmlns:mvc="sap.ui.core.mvc"
xmlns:f="sap.ui.layout.form"
controllerName="project1.controller.View1Detail"
height="100%">
<f:Form editable="true" class="sapUiSmallMargin" title="Product Detail">
<f:layout>
<f:ResponsiveGridLayout labelSpanXL="3" labelSpanL="3" labelSpanM="3" labelSpanS="12"
adjustLabelSpan="false" emptySpanXL="4" emptySpanL="4" emptySpanM="4" emptySpanS="0"
columnsXL="1" columnsL="1" columnsM="1" singleContainerFullSize="false" />
</f:layout>
<f:formContainers>
<f:FormContainer>
<f:formElements>
<f:FormElement label="ProductId" >
<f:fields>
<Input value="{/ProductId}" />
</f:fields>
</f:FormElement>
<f:FormElement label="Category" >
<f:fields>
<Input value="{/Category}" />
</f:fields>
</f:FormElement>
<f:FormElement label="MainCategory" >
<f:fields>
<Input value="{/MainCategory}" />
</f:fields>
</f:FormElement>
<f:FormElement label="TaxTarifCode" >
<f:fields>
<Input value="{/TaxTarifCode}" />
</f:fields>
</f:FormElement>
<f:FormElement label="SupplierName" >
<f:fields>
<Input value="{/SupplierName}" />
</f:fields>
</f:FormElement>
<f:FormElement label="WeightMeasure" >
<f:fields>
<Input value="{/WeightMeasure}" />
</f:fields>
</f:FormElement>
<f:FormElement label="WeightUnit" >
<f:fields>
<Input value="{/WeightUnit}" />
</f:fields>
</f:FormElement>
<f:FormElement label="Description" >
<f:fields>
<Input value="{/Description}" />
</f:fields>
</f:FormElement>
<f:FormElement label="Name" >
<f:fields>
<Input value="{/Name}" />
</f:fields>
</f:FormElement>
<f:FormElement label="DateOfSale" >
<f:fields>
<Input value="{/DateOfSale}" />
</f:fields>
</f:FormElement>
<f:FormElement label="ProductPicUrl" >
<f:fields>
<Input value="{/ProductPicUrl}" />
</f:fields>
</f:FormElement>
<f:FormElement label="Status" >
<f:fields>
<Input value="{/Status}" />
</f:fields>
</f:FormElement>
<f:FormElement label="Quantity" >
<f:fields>
<Input value="{/Quantity}" />
</f:fields>
</f:FormElement>
<f:FormElement label="UoM" >
<f:fields>
<Input value="{/UoM}" />
</f:fields>
</f:FormElement>
<f:FormElement label="CurrencyCode" >
<f:fields>
<Input value="{/CurrencyCode}" />
</f:fields>
</f:FormElement>
<f:FormElement label="Price" >
<f:fields>
<Input value="{/Price}" />
</f:fields>
</f:FormElement>
<f:FormElement label="Width" >
<f:fields>
<Input value="{/Width}" />
</f:fields>
</f:FormElement>
<f:FormElement label="Depth" >
<f:fields>
<Input value="{/Depth}" />
</f:fields>
</f:FormElement>
<f:FormElement label="Height" >
<f:fields>
<Input value="{/Height}" />
</f:fields>
</f:FormElement>
<f:FormElement label="DimUnit" >
<f:fields>
<Input value="{/DimUnit}" />
</f:fields>
</f:FormElement>
</f:formElements>
</f:FormContainer>
</f:formContainers>
</f:Form>
</mvc:View>
新建Controller
controller文件夹下新建js文件View1Detail.controller.js:
sap.ui.define([
"sap/ui/core/mvc/Controller",
'sap/m/MessageToast',
'./Formatter',
'sap/ui/model/json/JSONModel'
],
/**
* @param {typeof sap.ui.core.mvc.Controller} Controller
*/
function (Controller, MessageToast, Formatter, JSONModel) {
"use strict";
return Controller.extend("project1.controller.View1Detail", {
onInit: function () {
var oRouter = sap.ui.core.UIComponent.getRouterFor(this);
this.oRouter = oRouter;
oRouter.getRoute("View1Detail").attachPatternMatched(this._onObjectMatched, this);
},
_onObjectMatched: async function (oEvent) {
// set explored app's demo model on this sample
let parameter = oEvent.getParameter("arguments")
var paramJson = decodeURIComponent(parameter["parameters"]);
const param = JSON.parse(paramJson)
this.getView().setModel(new JSONModel(param.data));
},
});
});
注意这里需要注意的是 _onObjectMatched这个回调方法,在生产业务必须加上,否则会出现缓存问题,也就是点击清单第一条,进入了第一条数据详情,如果返回上一级页面,再点击第二条数据,进去看到的还是清单第一条的详情,会造成缓存问题。
新建Model
这里的model其实来源于清单页面点击后触发的函数来赋值的,具体代码如下:
sap.ui.define([
"sap/ui/core/mvc/Controller",
'sap/m/MessageToast',
'./Formatter',
'sap/ui/model/json/JSONModel'
],
/**
* @param {typeof sap.ui.core.mvc.Controller} Controller
*/
function (Controller, MessageToast, Formatter, JSONModel) {
"use strict";
return Controller.extend("project1.controller.View1", {
onInit: function () {
var oRouter = sap.ui.core.UIComponent.getRouterFor(this);
this.oRouter = oRouter;
},
onListItemPress: function (oEvent) {
MessageToast.show("Pressed : " + oEvent.getSource().getTitle());
let id = oEvent.oSource.oBindingContexts.products.sPath
let dataIndex = id.substr(id.lastIndexOf("/") + 1);
const products = this.getView().getModel("products");
const productCollection = products.getProperty("/ProductCollection");
const jsonParam = JSON.stringify({ data: productCollection[dataIndex] });
this.oRouter.navTo("View1Detail", { parameters: encodeURIComponent(jsonParam) })
}
});
});
可以看到,model数据来源于onListItemPress函数,通过获取当前页面的products模型的数据,再根据点击的行索引找到对应的详情数据,一encodeURI的形式,通过http参数的形式传递到下一个页面。
新建路由
修改manifest.json文件routing属性:
"routing": {
"config": {
"routerClass": "sap.m.routing.Router",
"viewType": "XML",
"async": true,
"viewPath": "project1.view",
"controlAggregation": "pages",
"controlId": "app",
"clearControlAggregation": false
},
"routes": [
{
"name": "RouteView1",
"pattern": ":?query:",
"target": [
"TargetView1"
]
},
{
"name": "View1Detail",
"pattern": "View1Detail:parameters:",
"target": "View1Detail"
}
],
"targets": {
"TargetView1": {
"viewType": "XML",
"transition": "slide",
"clearControlAggregation": false,
"viewId": "View1",
"viewName": "View1"
},
"View1Detail": {
"viewType": "XML",
"transition": "slide",
"clearControlAggregation": false,
"viewId": "View1Detail",
"viewName": "View1Detail"
}
}
},
详情页面预览
通过点击清单页面后,可显示产品详情:
点击选中行:
显示详情页面
结论
从零到一,新建了一个业务页面,有助于快速开放Fiori业务代码。
免费附件
products.json内容如下,无需使用vip下载:
{
"ProductCollection": [
{
"ProductId": "HT-1000",
"Category": "Laptops",
"MainCategory": "Computer Systems",
"TaxTarifCode": "1",
"SupplierName": "Very Best Screens",
"WeightMeasure": 4.2,
"WeightUnit": "KG",
"Description": "Notebook Basic 15 with 2,80 GHz quad core, 15\" LCD, 4 GB DDR3 RAM, 500 GB Hard Disc, Windows 8 Pro",
"Name": "Notebook Basic 15",
"DateOfSale": "2017-03-26",
"ProductPicUrl": "https://sdk.openui5.org/test-resources/sap/ui/documentation/sdk/images/HT-1000.jpg",
"Status": "Available",
"Quantity": 10,
"UoM": "PC",
"CurrencyCode": "EUR",
"Price": 956,
"Width": 30,
"Depth": 18,
"Height": 3,
"DimUnit": "cm"
},
{
"ProductId": "HT-1001",
"Category": "Laptops",
"MainCategory": "Computer Systems",
"TaxTarifCode": "1",
"SupplierName": "Very Best Screens",
"WeightMeasure": 4.5,
"WeightUnit": "KG",
"Description": "Notebook Basic 17 with 2,80 GHz quad core, 17\" LCD, 4 GB DDR3 RAM, 500 GB Hard Disc, Windows 8 Pro",
"Name": "Notebook Basic 17",
"DateOfSale": "2017-04-17",
"ProductPicUrl": "https://sdk.openui5.org/test-resources/sap/ui/documentation/sdk/images/HT-1001.jpg",
"Status": "Available",
"Quantity": 20,
"UoM": "PC",
"CurrencyCode": "EUR",
"Price": 1249,
"Width": 29,
"Depth": 17,
"Height": 3.1,
"DimUnit": "cm"
},
{
"ProductId": "HT-1002",
"Category": "Laptops",
"MainCategory": "Computer Systems",
"TaxTarifCode": "1",
"SupplierName": "Very Best Screens",
"WeightMeasure": 4.2,
"WeightUnit": "KG",
"Description": "Notebook Basic 18 with 2,80 GHz quad core, 18\" LCD, 8 GB DDR3 RAM, 1000 GB Hard Disc, Windows 8 Pro",
"Name": "Notebook Basic 18",
"DateOfSale": "2017-01-07",
"ProductPicUrl": "https://sdk.openui5.org/test-resources/sap/ui/documentation/sdk/images/HT-1002.jpg",
"Status": "Available",
"Quantity": 10,
"UoM": "PC",
"CurrencyCode": "EUR",
"Price": 1570,
"Width": 28,
"Depth": 19,
"Height": 2.5,
"DimUnit": "cm"
},
{
"ProductId": "HT-1003",
"Category": "Laptops",
"MainCategory": "Computer Systems",
"TaxTarifCode": "1",
"SupplierName": "Smartcards",
"WeightMeasure": 4.2,
"WeightUnit": "KG",
"Description": "Notebook Basic 19 with 2,80 GHz quad core, 19\" LCD, 8 GB DDR3 RAM, 1000 GB Hard Disc, Windows 8 Pro",
"Name": "Notebook Basic 19",
"DateOfSale": "2017-04-09",
"ProductPicUrl": "https://sdk.openui5.org/test-resources/sap/ui/documentation/sdk/images/HT-1003.jpg",
"Status": "Available",
"Quantity": 15,
"UoM": "PC",
"CurrencyCode": "EUR",
"Price": 1650,
"Width": 32,
"Depth": 21,
"Height": 4,
"DimUnit": "cm"
},
{
"ProductId": "HT-1007",
"Category": "Accessories",
"MainCategory": "Computer Components",
"TaxTarifCode": "1",
"SupplierName": "Technocom",
"WeightMeasure": 0.2,
"WeightUnit": "KG",
"Description": "Digital Organizer with State-of-the-Art Storage Encryption",
"Name": "ITelO Vault",
"DateOfSale": "2017-05-17",
"ProductPicUrl": "https://sdk.openui5.org/test-resources/sap/ui/documentation/sdk/images/HT-1007.jpg",
"Status": "Available",
"Quantity": 15,
"UoM": "PC",
"CurrencyCode": "EUR",
"Price": 299,
"Width": 32,
"Depth": 22,
"Height": 3,
"DimUnit": "cm"
},
{
"ProductId": "HT-1258",
"Category": "Smartphones and Tablets",
"MainCategory": "Smartphones & Tablets",
"TaxTarifCode": "1",
"SupplierName": "Ultrasonic United",
"WeightMeasure": 2.5,
"WeightUnit": "KG",
"Description": "8-inch Multitouch HD Screen (2000 x 1500) 32GB Internal Memory, Wireless N Wi-Fi, Bluetooth, GPS Enabled, 1.5 GHz Quad-Core Processor",
"Name": "Cepat Tablet 8",
"ProductPicUrl": "https://sdk.openui5.org/test-resources/sap/ui/documentation/sdk/images/HT-1258.jpg",
"Status": "Available",
"Quantity": 24,
"UoM": "PC",
"CurrencyCode": "EUR",
"Price": 529,
"Width": 38,
"Depth": 21,
"Height": 3.5,
"DimUnit": "cm"
}
],
"ProductCollectionStats": {
"Counts": {
"Total": 123,
"Weight": {
"Ok": 53,
"Heavy": 51,
"Overweight": 19
}
},
"Groups": {
"Category": {
"Accessories" : 34,
"Desktop Computers" : 7,
"Flat Screens" : 2,
"Keyboards" : 4,
"Laptops" : 11,
"Printers" : 9,
"Smartphones and Tablets" : 9,
"Mice" : 7,
"Computer System Accessories" : 8,
"Graphics Card" : 4,
"Scanners" : 4,
"Speakers" : 3,
"Software" : 8,
"Telekommunikation" : 3,
"Servers" : 3,
"Flat Screen TVs" : 3
},
"SupplierName": {
"Titanium": 21,
"Technocom": 22,
"Red Point Stores": 7,
"Very Best Screens": 14,
"Smartcards": 2,
"Alpha Printers": 5,
"Printer for All": 8,
"Oxynum": 8,
"Fasttech": 15,
"Ultrasonic United": 15,
"Speaker Experts": 3,
"Brainsoft": 3
}
},
"Filters": [
{
"type": "Category",
"values": [
{
"text": "Accessories",
"data": 34
},
{
"text": "Desktop Computers",
"data": 7
},
{
"text": "Flat Screens",
"data": 2
},
{
"text": "Keyboards",
"data": 4
},
{
"text": "Laptops",
"data": 11
},
{
"text": "Printers",
"data": 9
},
{
"text": "Smartphones and Tablets",
"data": 9
},
{
"text": "Mice",
"data": 7
},
{
"text": "Computer System Accessories",
"data": 8
},
{
"text": "Graphics Card",
"data": 4
},
{
"text": "Scanners",
"data": 4
},
{
"text": "Speakers",
"data": 3
},
{
"text": "Software",
"data": 8
},
{
"text": "Telekommunikation",
"data": 3
},
{
"text": "Servers",
"data": 3
},
{
"text": "Flat Screen TVs",
"data": 3
}
]
},
{
"type": "SupplierName",
"values": [
{
"text": "Titanium",
"data": 21
},
{
"text": "Technocom",
"data": 22
},
{
"text": "Red Point Stores",
"data": 7
},
{
"text": "Very Best Screens",
"data": 14
},
{
"text": "Smartcards",
"data": 2
},
{
"text": "Alpha Printers",
"data": 5
},
{
"text": "Printer for All",
"data": 8
},
{
"text": "Oxynum",
"data": 8
},
{
"text": "Fasttech",
"data": 15
},
{
"text": "Ultrasonic United",
"data": 15
},
{
"text": "Speaker Experts",
"data": 3
},
{
"text": "Brainsoft",
"data": 3
}
]
}
]
}
}