到目前为止,我们已经把所有的应用程序内容放在一个单一的页面。随着我们添加越来越多的功能,我们希望将内容分割,并将其放在单独的页面上。
webapp/manifest.json
{
"_version": "1.12.0",
…
"sap.ui5": {
…
"models": {
…
},
**"routing": {**
**"config": {**
"routerClass": "sap.m.routing.Router",
"viewType": "XML",
"viewPath": "sap.ui.demo.walkthrough.view",
"controlId": "app",
"controlAggregation": "pages",
"async": true
},
**"routes": [**
{
"pattern": "",
"name": "overview",
"target": "overview"
},
{
"pattern": "detail",
"name": "detail",
"target": "detail"
}
],
**"targets": {**
"overview": {
"viewId": "overview",
"viewName": "Overview"
},
**"detail": {**
"viewId": "detail",
"viewName": "Detail"
}
}
}
}
}
我们在描述符的sap.ui5部分添加了一个新的“选择途径”部分。有三个子部分定义了应用的选择途径和导航结构:
config
介绍全局途径选择的配置,以及适用于所有途径选择和目标的缺省值。我们定义了我们想要使用的途径选择类,以及视图在应用程序中的位置。为了自动加载和显示视图,我们还指定了哪个控件用于显示页面,以及在显示新页面时应该填充什么聚合。
routes
每个路径选择都定义了一个名称、一个模式和一个或多个目标,以便在路径选择时导航到这些目标。模式基本上是相匹配的URL部分路线,我们定义两个路线应用。第一个是默认路径,将显示overview页面内容与前面的步骤,和第二个是细节路线的URL模式的细节将显示一个新页面。
targets
目标定义一个视图显示,一个或多个路线,也可以手动从内部应用程序显示。当目标显示,相应的视图加载和应用程序所示。在我们的应用程序简单地定义两个目标视图名称对应于目标名称。
webapp/Component.js
sap.ui.define([
"sap/ui/core/UIComponent",
"sap/ui/model/json/JSONModel",
"./controller/HelloDialog"
], function (UIComponent, JSONModel, HelloDialog) {
"use strict";
return UIComponent.extend("sap.ui.demo.walkthrough.Component", {
metadata: {
manifest: "json"
},
init: function () {
// call the init function of the parent
UIComponent.prototype.init.apply(this, arguments);
// set data model
var oData = {
recipient: {
name: "World"
}
};
var oModel = new JSONModel(oData);
this.setModel(oModel);
// set dialog
this._helloDialog = new HelloDialog(this.getRootControl());
// create the views based on the url/hash
this.getRouter().initialize();
},
});
exit : function () {
this._helloDialog.destroy();
delete this._helloDialog;
},
openHelloDialog : function () {
this._helloDialog.open();
}
});
});
在组件初始化方法中,我们现在添加一个调用来初始化路径。我们不需要手动实例化路径,它会根据我们的AppDescriptor配置自动实例化并分配给组件。初始化路由器将计算当前URL并自动加载相应的视图。这是在AppDescriptor中配置的路径和目标的帮助下完成的。如果被路径被选择,则加载并显示对应目标的视图。
webapp/view/Overview.view.xml (New)
<mvc:View
controllerName="sap.ui.demo.walkthrough.controller.App"
xmlns="sap.m"
xmlns:mvc="sap.ui.core.mvc"
displayBlock="true">
<Shell>
<App class="myAppDemoWT" id="app"/>
</Shell>
</mvc:View>
我们的App视图现在只包含空的App标签。路由器会自动将当前URL对应的视图添加到应用控件中。路由器用对应于AppDescriptor中的属性controlId: " app "的ID来标识应用控件。
webapp/view/Detail.view.xml (New)
<mvc:View
xmlns="sap.m"
xmlns:mvc="sap.ui.core.mvc">
<Page
title="{i18n>detailPageTitle}">
<ObjectHeader
title="Invoice"/>
</Page>
</mvc:View>
现在我们为细节视图添加第二个视图。它只包含一个页面和一个控件控制显示静态发票文本。
webapp/i18n/i18n.properties
…
# Invoice List
invoiceListTitle=Invoices
invoiceStatusA=New
invoiceStatusB=In Progress
invoiceStatusC=Done
# Detail Page
detailPageTitle=Walkthrough - Details
我们将一个新字符串添加到资源包中,用于详细页面标题。
webapp/view/InvoiceList.view.xml
<mvc:View
controllerName="sap.ui.demo.walkthrough.controller.InvoiceList"
xmlns="sap.m"
xmlns:mvc="sap.ui.core.mvc">
<List …>
…
<items>
<ObjectListItem
title="{invoice>Quantity} x {invoice>ProductName}"
number="{
parts: [{path: 'invoice>ExtendedPrice'}, {path: 'view>/currency'}],
type: 'sap.ui.model.type.Currency',
formatOptions: {
showMeasure: false
}
}"
numberUnit="{view>/currency}"
numberState="{= ${invoice>ExtendedPrice} > 50 ? 'Error' : 'Success' }"
type="Navigation"
press="onPress">
<firstStatus>
<ObjectStatus text="{
path: 'invoice>Status',
formatter: '.formatter.statusText'
}"/>
</firstStatus>
</ObjectListItem>
</items>
</List>
</mvc:View>
在发票列表视图中,我们向列表项添加一个按钮事件,并将项目类型设置为导航,以便项目可以单击。
webapp/controller/InvoiceList.controller.js
sap.ui.define([
"sap/ui/core/mvc/Controller",
"sap/ui/model/json/JSONModel",
"../model/formatter",
"sap/ui/model/Filter",
"sap/ui/model/FilterOperator"
], function (Controller, JSONModel, formatter, Filter, FilterOperator) {
"use strict";
return Controller.extend("sap.ui.demo.walkthrough.controller.InvoiceList", {
…
onPress: function (oEvent) {
var oRouter = sap.ui.core.UIComponent.getRouterFor(this);
oRouter.navTo("detail");
}
});
});
我们将事件处理程序函数添加到发票列表的控制器中。现在是时候通过单击发票列表中的一个项目导航到详细信息页面了。我们通过调用helper方法sap.ui.core.UIComponent.getRouterFor(this)来访问应用的路由器实例。在路由器上,我们调用navTo方法来导航到我们在路由配置中指定的详细路径。当您单击发票列表中的一个项目时,您现在应该会看到详细信息页面。
注意
在描述符中定义路由配置
现在我们可以在概览和详细信息页面之间导航,但是我们在概览中选择的实际项目还没有显示在详细信息页面上。我们的应用程序的一个典型用例是在详细页面上显示所选项目的附加信息。
1.练习效果
webapp/manifest.json
{
"_version": "1.12.0",
…
"sap.ui5": {
…
"routing": {
"config": {
"routerClass": "sap.m.routing.Router",
"viewType": "XML",
"viewPath": "sap.ui.demo.walkthrough.view",
"controlId": "app",
"controlAggregation": "pages",
"async": true
},
"routes": [
{
"pattern": "",
"name": "overview",
"target": "overview"
},
{
"pattern": "detail/{invoicePath}",
"name": "detail",
"target": "detail"
}
],
"targets": {
"overview": {
"viewID": "overview"
"viewName": "Overview"
},
"detail": {
"viewId": "detail"
"viewName": "Detail"
}
}
}
}
}
现在,我们向详细路由添加一个导航参数invoicePath,这样我们就可以将所选项目的信息传递给详细页面。强制导航参数用大括号定义。
webapp/view/Detail.view.xml
<mvc:View
controllerName="sap.ui.demo.walkthrough.controller.Detail"
xmlns="sap.m"
xmlns:mvc="sap.ui.core.mvc">
<Page
title="{i18n>detailPageTitle}">
<ObjectHeader
intro="{invoice>ShipperName}"
title="{invoice>ProductName}"/>
</Page>
</mvc:View>
我们添加了一个控制器,它将负责在视图上设置项目的上下文,并将ObjectHeader的一些属性绑定到发票模型的字段。我们可以在这里添加来自invoice对象的更详细的信息,但是为了简单起见,我们现在只显示两个字段。
webapp/controller/InvoiceList.controller.js
sap.ui.define([
"sap/ui/core/mvc/Controller",
"sap/ui/model/json/JSONModel",
"../model/formatter",
"sap/ui/model/Filter",
"sap/ui/model/FilterOperator"
], function (Controller, JSONModel, formatter, Filter, FilterOperator) {
"use strict";
return Controller.extend("sap.ui.demo.walkthrough.controller.InvoiceList", {
…
onPress: function (oEvent) {
var oItem = oEvent.getSource();
var oRouter = sap.ui.core.UIComponent.getRouterFor(this);
oRouter.navTo("detail", { "用NAVTO指定详细路径
invoicePath: oItem.getBindingContext("invoice").getPath().substr(1)
});
}
});
});
与之交互的控制实例可以被所有SAPUI5可用事件的getSource方法访问。被点击的ObjectListItem会返回在例子中。
我们将使用它将被单击项目的信息传递到详细信息页面,以便在那里可以显示相同的项目。
在navTo方法中,我们现在添加一个配置对象,用项目的当前信息填充导航参数invoicePath。这将更新URL并同时导航到详细视图。
在详细信息页面上,我们可以再次访问此上下文信息并显示相应的项。
为了标识所选择的对象,我们通常会使用后端系统中项的键,因为它既短又精确。然而,对于我们的发票项目,我们没有一个简单的键,而是直接使用绑定路径来保持示例的简短和简单。条目的路径是绑定上下文的一部分,绑定上下文是SAPUI5的一个助手对象,用于管理控件的绑定信息。绑定上下文可以通过在任何绑定的SAPUI5控件上调用带有模型名的getBindingContext方法来访问。我们需要移除第一个/从绑定路径调用。substr(1)在字符串上,因为这是url中的特殊字符,是不允许的,所以我们将在详细页面上再次添加它。
webapp/controller/Detail.controller.js (New)
sap.ui.define([
"sap/ui/core/mvc/Controller",
"sap/ui/core/UIComponent"
], function (Controller, UIComponent) {
"use strict";
return Controller.extend("sap.ui.demo.walkthrough.controller.Detail", {
onInit: function () {
var oRouter = sap.ui.core.UIComponent.getRouterFor(this);
oRouter.getRoute("detail").attachPatternMatched(this._onObjectMatched, this);
},
_onObjectMatched: function (oEvent) {
this.getView().bindElement({
path: "/" + oEvent.getParameter("arguments").invoicePath,
model: "invoice"
});
}
});
});
我们的最后一块拼图是细节控制器。它需要在视图上设置我们通过URL参数invoicePath传入的上下文,以便实际显示发票列表中选中的项目,否则,视图将保持空状态。
在控制器的onInit方法中,我们获取应用路由器的实例,并通过调用attachPatternMatched方法来附加到detail路由上。我们注册了一个内部回调函数_onObjectMatched将在路由被选中时执行,或者通过点击该条目,或者通过调用带有详细页面URL的应用程序。
在路由器触发的_onObjectMatched方法中,我们接收到一个可以用来访问URL和导航参数的事件。arguments参数将返回一个与路由模式中的导航参数相对应的对象。我们访问在发票列表控制器中设置的invoicePath,并调用视图上的bindElement函数来设置上下文。我们必须再次在路径前面添加根/,因为它作为URL参数传递路径时被删除了。
bindElement函数为SAPUI5控件创建绑定上下文,并接收模型名称以及配置对象中条目的路径。这将触发与发票模型的字段连接的UI控件的更新。当您单击发票列表中的一个项目时,您现在应该在一个单独的页面上看到发票的详细信息。
注意
在AppDescriptor中定义路径配置
现在,我们可以导航到详细信息页面并显示发票,但还不能返回到概述页面。我们将向详细页面添加一个后退按钮,并实现一个函数来再次显示概览页面。
webapp/view/Detail.view.xml
<mvc:View
controllerName="sap.ui.demo.walkthrough.controller.Detail"
xmlns="sap.m"
xmlns:mvc="sap.ui.core.mvc">
<Page
title="{i18n>detailPageTitle}"
showNavButton="true"
**navButtonPress=".onNavBack">**
<ObjectHeader
intro="{invoice>ShipperName}"
title="{invoice>ProductName}"/>
</Page>
</mvc:View>
在详细信息页面上,我们通过将参数showNavButton设置为true来告诉控件显示一个后退按钮,并注册一个当按下后退按钮时调用的事件处理程序。
webapp/controller/Detail.controller.js
sap.ui.define([
"sap/ui/core/mvc/Controller",
"sap/ui/core/routing/History",
"sap/ui/core/UIComponent"
], function (Controller, History, UIComponent) {
"use strict";
return Controller.extend("sap.ui.demo.walkthrough.controller.Detail", {
onInit: function () {
var oRouter = UIComponent.getRouterFor(this);
oRouter.getRoute("detail").attachPatternMatched(this._onObjectMatched, this);
},
_onObjectMatched: function (oEvent) {
this.getView().bindElement({
path: "/" + oEvent.getParameter("arguments").invoicePath,
model: "invoice"
});
},
**onNavBack: function () {**
var oHistory = History.getInstance();
var sPreviousHash = oHistory.getPreviousHash();
if (sPreviousHash !== undefined) {
window.history.go(-1);
} else {
var oRouter = UIComponent.getRouterFor(this);
oRouter.navTo("overview", {}, true);
}
}
});
});
我们加载一个新的依赖项,它帮助我们从sap.ui.core.routing命名空间管理导航历史,并将事件处理程序的实现添加到我们的细节页面控制器。
在事件处理程序中,我们访问导航历史记录,并尝试确定前面的排列。与浏览器历史相比,只有当应用程序中的导航步骤已经发生时,我们才会得到一个有效的结果。然后,我们将简单地使用浏览器历史记录返回到前一页。如果之前没有导航,我们可以告诉路由器直接进入我们的概览页面。第三个参数true告诉路由器用新的历史状态替换当前的历史状态,因为实际上我们自己做了一个返回导航。第二个参数是一个空数组({})。
对于我们的用例来说,这个实现比浏览器的后退按钮要好一些。浏览器只会返回上一步,尽管在另一个页面之外的另一页,我们总是想回到overview页面即使我们来自另一个链接或直接与书签打开详细信息页面。你可以直接在新标签中加载详细信息页面点击应用程序中的后退按钮,它仍然会回到概览页面。
注意
在历史记录状态不清楚时添加返回父页面的路径。