克米亚sap论坛,中国最好的sap论坛,sap系统,sap培训,kemiya,克米亚,sap账号,sap ides,sap mm,sap hana,sap fico,sap pp

 找回密码
 注册
查看: 45|回复: 2

SAPUI5 (22) - Routing 实现多页面导航

[复制链接]
kmy 发表于 2018-12-24 09:26:16 | 显示全部楼层 |阅读模式

前面我们实现了基于组件的多页面程序,这个程序还有两个主要的缺点:

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"                }            }        }    }}1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798

解释一些重要的配置:

1. 资源包文件

资源包文件的设置有两个地方:

"sap.app": {    "_version": "1.1.0",    "id": "stone.sapui5.test",    "type": "application",    "i18n": "i18n/i18n.properties",    ...    },1234567

这里设置的是资源包文件的路径和文件名。使用的相对于 manifest.json
文件的相对路径。

另外一个地方在 sapui5.models:

"sap.ui5": {            ...    "models": {        ...        "i18n": {            "type": "sap.ui.model.resource.ResourceModel",            "settings": {                "bundleName": "webapp.i18n.i18n"            }        }    }1234567891011

设置名称为 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"                }            }        }12345678910111213

一个是没有指定名称的 model,当 view 中数据绑定时,没有给出前缀的时候,就参照到这个 model。比如  就参照到 model 所加载的数据中第一个 Supplier id。这个 model 的
dataSource 是在 sap.app 部分设置的 dataSource:

"sap.app": {    ...    "dataSources": {        "mainService": {            "uri": "./service/data.json",            "type": "JSON"        }    }12345678

dataSource 为./service文件夹下面的 data.json 文件。

第二个是刚才提到的 Resource Model: i18n。

3. Root View"sap.ui5": {    "_version": "1.1.0",    "rootView": {        "viewName": "webapp.view.App",        "type": "XML"    },123456

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"                }            }        }1234567891011121314151617181920212223242526272829303132333435363738

比较直观,不懂的地方可以参照 Routing Configuration 。

Componet.js 文件

这个文件主要的变化是将 metadata 的设置、resource model 的设置、root view 的设置都转移到 manifest.json 中,所以 Component 中 一条语句完成初始化:

this.getRouter().initialize();1

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();        }    });});1234567891011121314151617181920212223Root View          1234567

根据 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);    }12345678910

变更后 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)                });;             }         });        });123456789101112131415161718192021

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}"....12

所以使用 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});            }                   });    });12345678910111213141516171819202122232425262728293031323334353637

代码说明:

  • 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});}   12345
  • 当用户点击导航按钮,判断是否有上一个路径 ( 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);    }}1234567891011源代码

22_zui5_routing_master_detail

参考
IT猎头-JACK 发表于 2019-1-9 13:28:59 | 显示全部楼层
克米亚sap论坛
ganvllemt 发表于 7 小时前 | 显示全部楼层
sap是什么意思
您需要登录后才可以回帖 登录 | 注册

本版积分规则

QQ|克米亚sap论坛,sap账号,sap系统,sap ides,sap学习机,sap练习环境 ( 渝ICP备18002525号-5 )

GMT+8, 2019-1-18 18:32

Powered by Discuz! X3.4

© 2001-2017 Comsenz Inc.

快速回复 返回顶部 返回列表