SAPUI5 (22) - Routing 实现多页面导航
2017-02-25 15:11
211 查看
前面我们实现了基于组件的多页面程序,这个程序还有两个主要的缺点:
1)存在全局变量
2)多个页面全部在程序启动时加载,多个页面也是在同一个 URL 下由
Openui5 框架进行管理,这种模式满足不了多页面的需求。解决的办法是:路由 ( routing ) 。通过 routing 实现多页面的导航,异步实现按需加载。
本篇仍然基于之前 Master-detail 供应商显示这支程序进行重构,使用 routing 实现页面间导航,将 Component 的元数据(metadata),移到专门的文件中。
硬编码模式:页面之间根据模式导航,没有参数传递,比如
路径含有必输参数模式:模式中 大括号({}) 包含的部分表示参数必须输入。比如
路径含有可选参数模式:模式中 冒号 包含的部分为必输参数。比如
以及
路径含有查询参数模式:查询参数 ( query parameter ) 在问号之后。比如
通配参数模式:以星号结尾的参数是通配参数,通配参数将根据模式尽可能匹配。
解释一些重要的配置:
这里设置的是资源包文件的路径和文件名。使用的相对于
文件的相对路径。
另外一个地方在
设置名称为 i18n 的 resource model,
一个是没有指定名称的 model,当 view 中数据绑定时,没有给出前缀的时候,就参照到这个 model。比如
dataSource 为
第二个是刚才提到的 Resource Model: i18n。
Root view (启动即显示的 view):类型为 xml,名称为 App。OpenUI5 在相应文件夹下面查找名为
Root view 是程序启动的重要设置,启动的流程如下:
index.html 的 ComponentContainer 根据
Component 的 metadata 指向设定的
的 root view 为
App view 并不需要像之前文章介绍的内嵌 master view 和 detail view,而是由路由器根据路径在 pattern 中找匹配的模式,在 target 中找对应的
view 加载。
比较直观,不懂的地方可以参照 Routing Configuration 。
根据
view 中只需要申明
变更后 Master controller 的代码如下:
Master controller 在行项目被点击之后,要完成两个任务:
- 跳转到 Detail view
- 向 Detail view 传递一个参数,这个参数是当前点击的路径,Detail view
获取这个路径,完成数据的绑定。
所有这些,都通过 router 来完成。
-
-
detail view。
-
所以使用
Detail.controller.js 的代码:
代码说明:
Detail view 主要负责两件事:
1) 获取 Master view 传递的路径,根据此路径完成 element binding。比如当 Master view 传过来
2) 根据页面之间的关系,当点击 返回 按钮时,返回到上一个页面。
当用户点击导航按钮,判断是否有上一个路径 ( previous hash ),如果有就返回上一个路径,否则跳转到 Master view:
Get started with SAPUI5 and Routing
[Routing with Parameters](
https://openui5.hana.ondemand.com/#docs/guide/2366345a94f64ec1a80f9d9ce50a59ef.html)
Nested UI Routing in OpenUI5
[Creating modularized SAPUI5 applications with XML Views, Routing and i18n](
https://www.nabisoft.com/tutorials/sapui5/creating-modularized-sapui5-applications-with-xml-views-routing-and-i18n)
1)存在全局变量
oApp (sap.m.App);
2)多个页面全部在程序启动时加载,多个页面也是在同一个 URL 下由
Openui5 框架进行管理,这种模式满足不了多页面的需求。解决的办法是:路由 ( routing ) 。通过 routing 实现多页面的导航,异步实现按需加载。
本篇仍然基于之前 Master-detail 供应商显示这支程序进行重构,使用 routing 实现页面间导航,将 Component 的元数据(metadata),移到专门的文件中。
OpenUi5 的 Routing
Openui5 的 routing 基于模式 ( pattern ),使用#符号表示不同的路径 ( route ),导航通过路径的改变来实现。
Pattern 表达式
Openui5 一共有 5 种pattern表达式:
硬编码模式:页面之间根据模式导航,没有参数传递,比如
product/settings表示导航到产品配置。
路径含有必输参数模式:模式中 大括号({}) 包含的部分表示参数必须输入。比如
product/{id}表示导航到产品某一 id,比如
product/5表示 id 为 5 的产品,id 为必输。
路径含有可选参数模式:模式中 冒号 包含的部分为必输参数。比如
product/{id}/detail/:detailId:,
detailId为可选参数。
product/5/detail
以及
product/3/detail/2都能与此模式匹配。
路径含有查询参数模式:查询参数 ( query parameter ) 在问号之后。比如
product{?query},query 这个参数为必输项。
product:?query:中的 query 这个参数为可选参数。
通配参数模式:以星号结尾的参数是通配参数,通配参数将根据模式尽可能匹配。
Routing 实现 Master-detail 界面
下图来自网络,很好地说明了 routing 中的项目文件结构:Application Descriptor
manifest.json文件配置应用程序的很多信息,被称为 Application Descriptor 。先给出文件的全部内容:
{ "_version": "1.1.0", "sap.app": { "_version": "1.1.0", "id": "stone.sapui5.test", "type": "application", "i18n": "i18n/i18n.properties", "applicationVersion": { "version": "1.0.0" }, "title": "{{appTitle}}", "description": "{{appDescription}}", "dataSources": { "mainService": { "uri": "./service/data.json", "type": "JSON" } } }, "sap.ui": { "_version": "1.1.0", "technology": "UI5", "deviceTypes": { "desktop": true, "tablet": true, "phone": true }, "supportedThemes": [ "sap_bluecrystal" ] }, "sap.ui5": { "_version": "1.1.0", "rootView": { "viewName": "webapp.view.App", "type": "XML" }, "dependencies": { "minUI5Version": "1.30.0", "libs": { "sap.m": {} } }, "contentDensities": { "compact": true, "cozy": true }, "models": { "": { "dataSource": "mainService" }, "i18n": { "type": "sap.ui.model.resource.ResourceModel", "settings": { "bundleName": "webapp.i18n.i18n" } } }, "routing": { "config": { "routerClass": "sap.m.routing.Router", "viewType": "XML", "viewPath": "webapp.view", "controlId": "app", "controlAggregation": "pages", "bypassed": { "target": "notFound" } }, "routes": [{ "pattern": "", "name": "master", "target": "master" }, { "pattern": "detail/{supplierPath}", "name": "detail", "target": "detail" }], "targets": { "master": { "viewName": "Master", "viewLevel": 1 }, "detail": { "viewName": "Detail", "viewLevel": 2 }, "notFound": { "viewName": "NotFound", "viewId": "notFound" } } } } }
解释一些重要的配置:
1. 资源包文件
资源包文件的设置有两个地方:"sap.app": { "_version": "1.1.0", "id": "stone.sapui5.test", "type": "application", "i18n": "i18n/i18n.properties", ... },
这里设置的是资源包文件的路径和文件名。使用的相对于
manifest.json
文件的相对路径。
另外一个地方在
sapui5.models:
"sap.ui5": { ... "models": { ... "i18n": { "type": "sap.ui.model.resource.ResourceModel", "settings": { "bundleName": "webapp.i18n.i18n" } } }
设置名称为 i18n 的 resource model,
bundleName后面是根据
index.html文件的 resource roots 设置的相对路径。然后在代码中添加对
ResourceBundle的依赖后,通过
{i18n>xxx}实现绑定。
2. Models
manifest.json文件共设置了两个 model:
"sap.ui5": { ... "models": { "": { "dataSource": "mainService" }, "i18n": { "type": "sap.ui.model.resource.ResourceModel", "settings": { "bundleName": "webapp.i18n.i18n" } } }
一个是没有指定名称的 model,当 view 中数据绑定时,没有给出前缀的时候,就参照到这个 model。比如
<Text text="{/Suppliers/0/id}" />就参照到 model 所加载的数据中第一个 Supplier id。这个 model 的
dataSource是在
sap.app部分设置的 dataSource:
"sap.app": { ... "dataSources": { "mainService": { "uri": "./service/data.json", "type": "JSON" } }
dataSource 为
./service文件夹下面的
data.json文件。
第二个是刚才提到的 Resource Model: i18n。
3. Root View
"sap.ui5": { "_version": "1.1.0", "rootView": { "viewName": "webapp.view.App", "type": "XML" },
Root view (启动即显示的 view):类型为 xml,名称为 App。OpenUI5 在相应文件夹下面查找名为
App.view.xml文件并加载。通过这种方式,实现了 root view 的配置化。
Root view 是程序启动的重要设置,启动的流程如下:
index.html 的 ComponentContainer 根据
name或
component属性实例化 Component
Component 的 metadata 指向设定的
manifest.json文件
manifest.json文件的
sap.ui5>rootView设定了启动时候加载并显示
的 root view 为
App.view.xml
App view 并不需要像之前文章介绍的内嵌 master view 和 detail view,而是由路由器根据路径在 pattern 中找匹配的模式,在 target 中找对应的
view 加载。
4. Routing 设置
"sap.ui5": { ... "routing": { "config": { "routerClass": "sap.m.routing.Router", "viewType": "XML", "viewPath": "webapp.view", "controlId": "app", "controlAggregation": "pages", "bypassed": { "target": "notFound" } }, "routes": [{ "pattern": "", "name": "master", "target": "master" }, { "pattern": "detail/{supplierPath}", "name": "detail", "target": "detail" }], "targets": { "master": { "viewName": "Master", "viewLevel": 1 }, "detail": { "viewName": "Detail", "viewLevel": 2 }, "notFound": { "viewName": "NotFound", "viewId": "notFound" } } }
比较直观,不懂的地方可以参照 Routing Configuration 。
Componet.js 文件
这个文件主要的变化是将 metadata 的设置、resource model 的设置、root view 的设置都转移到manifest.json中,所以 Component 中 一条语句完成初始化:
this.getRouter().initialize();
manifest.json文件的全部代码:
sap.ui.define([ "sap/ui/core/UIComponent", "sap/ui/model/resource/ResourceModel", "sap/ui/model/json/JSONModel" ], function (UIComponent, ResourceModel, JSONModel) { "use strict"; return UIComponent.extend("webapp.Component", { metadata: { manifest: "json" }, init : function () { // call the base component's init function UIComponent.prototype.init.apply(this, arguments); // create the views based on the url/hash this.getRouter().initialize(); } }); });
Root View
<core:View xmlns:core="sap.ui.core" xmlns:mvc="sap.ui.core.mvc" xmlns="sap.m" displayBlock="true" xmlns:html="http://www.w3.org/1999/xhtml"> <App id="app" /> </core:View>
根据
manifest.json的 root view 设置,
App.view.xml是 root view,在
view 中只需要申明
sap.m.App,id 为
app。Master view 和 Detail view不申明,由 routing 根据路径自动加载。
Master Controller 和 Detail Controller
Master view 和 Detail view 代码没有改变。但 Master controller 和 Detail controller 的代码需要改变。前一篇是通过 oApp(sap.m.Agg) 这个全局变量来导航,通过 oApp 管理页面。回顾一下 Master controller 中
onListPess事件处理程序的代码:
onListPress: function(oEvent){ // 跳转到detail view var sPageId = oApp.getPages()[1].getId(); oApp.to(sPageId); // 设置detail page的bindingContext var oContext = oEvent.getSource().getBindingContext(); var oDetailPage = oApp.getPage(sPageId); oDetailPage.setBindingContext(oContext); }
变更后 Master controller 的代码如下:
sap.ui.define([ "sap/ui/core/mvc/Controller", "sap/ui/core/UIComponent" ], function(Controller, UIComponent){ "use strict"; return Controller.extend("webapp.controller.Master", { onListPress: function(oEvent){ var oRouter = UIComponent.getRouterFor(this); var oItem = oEvent.getSource(); var sPath = oItem.getBindingContext().getPath(); oRouter.navTo("detail", { supplierPath: encodeURIComponent(sPath) });; } }); } );
Master controller 在行项目被点击之后,要完成两个任务:
- 跳转到 Detail view
- 向 Detail view 传递一个参数,这个参数是当前点击的路径,Detail view
获取这个路径,完成数据的绑定。
所有这些,都通过 router 来完成。
-
var oRouter = UIComponent.getRouterFor(this);获取当前的 router
-
var oItem = oEvent.getSource()获取点击所在的行,然后
oItem.getBindingContext().getPath()获取点击行的路径 (string类型) 。比如,当用户点击第一行,sPath 为
/Suppliers/0。这个路径需要传递到
detail view。
-
oRouter.navTo()方法不能包含
/(这是一个特殊的字符),否则提示如下错误。
Uncaught Error: Invalid value "/Suppliers/0" for segment "{supplierPath}". ...
所以使用
encodeURIComponent()函数编码,在Detail controller 中用
decodeURIComponen()t函数解码。
Detail.controller.js 的代码:
sap.ui.define([ "sap/ui/core/mvc/Controller", "sap/ui/core/UIComponent", "sap/ui/core/routing/History" ], function(Controller, UIComponent, History){ "use strict"; return Controller.extend("webapp.controller.Detail", { onInit: function(){ var oRouter = UIComponent.getRouterFor(this); oRouter.getRoute("detail") .attachPatternMatched(this._onObjectMatched, this); }, onNavPress: function() { var oHistory = History.getInstance(); var sPreviousHash = oHistory.getPreviousHash(); if (sPreviousHash != undefined){ window.history.go(-1); }else{ var oRouter = UIComponent.getRouterFor(this); oRouter.navTo("master",{}, true); } }, _onObjectMatched: function (oEvent) { var sPath = decodeURIComponent( oEvent.getParameter("arguments").supplierPath); this.getView().bindElement({ path: sPath}); } }); } );
代码说明:
Detail view 主要负责两件事:
1) 获取 Master view 传递的路径,根据此路径完成 element binding。比如当 Master view 传过来
/Suppliers/0,则与第一条数据绑定;
2) 根据页面之间的关系,当点击 返回 按钮时,返回到上一个页面。
onInit()event handler中:
oRouter.getRoute("detail").attachPatternMatched(this._onObjectMatched, this);,当模式匹配时,附加事件处理器为
_onObjectMatched。然后在
_onObjectMatched中获取 Master view 传递的路径并绑定数据。
_onObjectMatched: function (oEvent) { var sPath = decodeURIComponent( oEvent.getParameter("arguments").supplierPath); this.getView().bindElement({path: sPath}); }
当用户点击导航按钮,判断是否有上一个路径 ( previous hash ),如果有就返回上一个路径,否则跳转到 Master view:
onNavPress: function() { var oHistory = History.getInstance(); var sPreviousHash = oHistory.getPreviousHash(); if (sPreviousHash != undefined){ window.history.go(-1); }else{ var oRouter = UIComponent.getRouterFor(this); oRouter.navTo("master",{}, true); } }
源代码
22_zui5_routing_master_detail参考
Routing and NavigationGet started with SAPUI5 and Routing
[Routing with Parameters](
https://openui5.hana.ondemand.com/#docs/guide/2366345a94f64ec1a80f9d9ce50a59ef.html)
Nested UI Routing in OpenUI5
[Creating modularized SAPUI5 applications with XML Views, Routing and i18n](
https://www.nabisoft.com/tutorials/sapui5/creating-modularized-sapui5-applications-with-xml-views-routing-and-i18n)
相关文章推荐
- SAPUI5 (19) - 多页面程序及简单的页面导航
- 页面导航实现页面局部内容改变
- SAPUI5实现标准应用View替换的开发实践
- Android基于基于布局嵌套的页面导航实现
- 采用TabHost和RadioButton实现页面导航效果
- CSS导航布局中当前页面的具体实现demo示例
- jQuery+css实现百度百科的页面导航效果
- Android仿微信页面底部导航效果代码实现
- wordpress不用插件实现Pagenavi页面导航功能
- 基于Jquery的$.cookie()实现跨越页面tabs导航实现代码
- [Aaronyang] 写给自己的WPF4.5 笔记17[Page实现页面导航]
- 源生实现滚动页面显示悬浮导航
- Android 中ActionBar+fragment实现页面导航的实例
- Android 实现类似微信页面底部导航效果
- Android使用ViewPager实现带指示点的页面导航效果
- 57-003-1 bootstrap附加导航Affix实现页面的滚动监听
- 使用框架集实现页面导航功能
- 基于jquery实现页面滚动时顶部导航显示隐藏
- 【Win10】页面导航的实现
- ios 实现导航控制器带动画的推送页面