• 导航与路由1-9 (渣翻)


    整理一下导航与路由的资料

    https://sapui5.netweaver.ondemand.com/#/topic/1b6dcd39a6a74f528b27ddb22f15af0d

    在传统的Web应用程序中,服务器根据请求的URL模式确定请求的资源,并相应地提供服务。服务器端逻辑控制如何以适当的方式显示请求的资源或页面。

    在单页应用程序中,最初只从服务器请求一个页面,并且使用客户端逻辑动态加载其他资源。用户只能在此页面内导航。导航内容将保存在hash(#),用来代替服务器路径或URL参数。

    关于hash可以参考这个bolg。https://www.cnblogs.com/hsprout/p/5837253.html

    因为是单页面的程序,所以只需要使用hash来指定页面位置。有两个方法,一是使用锚点,比如<a name="user"></a>,二是使用id属性,比如<div id="user" ></div>。

    例如,一个传统的web应用程序,使用url http:///< something -path to-the-app>/employees/resume.html?id=3 或 http:///< some-pathto -the-app>/employees/3/resume来显示员工的resume。

    如果使用单页程序,可以基于hash的url http://<your-host>/<some-path-to-the-app>/#/employees/3/resume 来做同样的事情。

    hash就是指在#后面的内容,这部分内容由路由解释。

    官网提供了一个例子,来介绍这部分内容,这里一步一步来进行实现。

    Step 1: Set Up the Initial App

    将官网提供的代码下载到本地,并且运行。

    代码结构

    运行结果

    Step 2: Enable Routing

    修改manifest.json

    {
       "_version": "1.12.0",
       "sap.app": {
          ...
       },
       "sap.ui": {
          ...
       },
       "sap.ui5": {
    
          "rootView": {
              "viewName": "sap.ui.demo.nav.view.App",
              "type": "XML",
              "async": true,
              "id": "app"
          },
          "dependencies": {
             ...
          },
          "models": {
             ...
          },
          "routing": {
             "config": {
                "routerClass": "sap.m.routing.Router",
                "viewType": "XML",
                "viewPath": "sap.ui.demo.nav.view",
                "controlId": "app",
                "controlAggregation": "pages",
                "transition": "slide",
                "async": true
             },
             "routes": [{
                "pattern": "",
                "name": "appHome",
                "target": "home"
             }],
             "targets": {
                "home": {
                   "viewId": "home",
                   "viewName": "Home",
                   "viewLevel" : 1
                }
             }
          }
       }
    }

    sapui5的单页程序,可以使用所谓的"router"通过对基于hash的url进行一个或多个视图的转发。因此,路由器需要知道如何定位和显示视图。

    可以在manifest.json文件中对路由进行配置,主要有三个属性对程序进行配置。

    config

      routerClass的默认值是sap.ui.core.routing.Router,因为我们实现了一个基于sap.m的应用程序。routerClass的可能值是 sap.ui.core.routing.Router,sap.m.routing.Router,或sap.ui.core.routing.Router的任何其他子类。

      与sap.ui.core.routing.Router相比,sap.m.routing.Router对移动应用程序进行了优化,添加了属性:viewLevel,transition,transitionParameters 

      viewPath定义了视图在程序中的位置。

      为了自动加载和显示视图,我们还指定了用于显示页面的控件的controlId,与该控件的controlAggregation,当新页面显示的时候,他会被填充。

      因为我们使用的是xml view所以这里viewType指定为xml。

      transition为页面的切换模式。

      如果需要的话,所以的config参数都可以在单独的路由定义中重写。  

    routes

      每个路由定义一个name、一个pattern和一个或多个target,以便在路由被触发时导航到这些目标。

      pattern是匹配到路由的hash部分。

      路由的位置非常重要,因为第一个被匹配到的路由就是要使用的路由。

      在这个例子中,用一个空的pattern还匹配空的hash。

      name属性允许您选择惟一的路由名称,该名称可以帮助您导航特定的路由。

      target属性引用targets的一个或多个目标。

    targets

      定义要显示的视图,与routes关联,不管何时显示都会将其加载到使用控件的controlAggregation中,这个控件是通过controlId来配置的。

      每个target都有一个唯一的key,这里是home。

      viewName用来指定要加载的view,由viewPath+viewName决定。

      viewLevel用来指定transition的翻转方向,不是必须的。

    修改Component.js

    sap.ui.define([
        "sap/ui/core/UIComponent"
    ], function (UIComponent) {
        "use strict";
    
        return UIComponent.extend("sap.ui.demo.nav.Component", {
    
            metadata: {
                manifest: "json"
            },
    
            init: function () {
                // call the init function of the parent
                UIComponent.prototype.init.apply(this, arguments);
    
                // create the views based on the url/hash
                this.getRouter().initialize();
            }
        });
    });

    在init函数中,先调用父类的init函数,可以获得路由的引用,并且调用initialize() 对其进行初始化,路由会自动实例化。应用程序自动启动,路由配置跟路由事件。

    修改App.view.xml

    <mvc:View
        controllerName="sap.ui.demo.nav.controller.App"
        xmlns="sap.m"
        xmlns:mvc="sap.ui.core.mvc"
        displayBlock="true">
        <Shell>
            <App id="app"/>
        </Shell>
    </mvc:View>

    创建Home.view.xml

    <mvc:View
       controllerName="sap.ui.demo.nav.controller.Home"
       xmlns="sap.m"
       xmlns:mvc="sap.ui.core.mvc">
       <Page title="{i18n>homePageTitle}" class="sapUiResponsiveContentPadding">
          <content>
             <Button text="{i18n>iWantToNavigate}" class="sapUiTinyMarginEnd"/>
          </content>
       </Page>
    </mvc:View>

    创建Home.controller.js

    sap.ui.define([
       "sap/ui/core/mvc/Controller"
    ], function (Controller) {
       "use strict";
    
       return Controller.extend("sap.ui.demo.nav.controller.Home", {
    
       });
    
    });

    Step 3: Catch Invalid Hashes

    检测无效的hash,并可视化。

    修改manifest.json

    {
       ...
       "sap.ui5": {
          ...
          "routing": {
             "config": {
                "routerClass": "sap.m.routing.Router",
                "viewType": "XML",
                "viewPath": "sap.ui.demo.nav.view",
                "controlId": "app",
                "controlAggregation": "pages",
                "transition": "slide",
                "bypassed": {
                   "target": "notFound"
                },
                "async": true
             },
             "routes": [{
                "pattern": "",
                "name": "appHome",
                "target": "home"
             }],
             "targets": {
                "home": {
                   "viewId": "home",
                   "viewName": "Home",
                   "viewLevel" : 1
                },
                "notFound": {
                   "viewId": "notFound",
                   "viewName": "NotFound",
                   "transition": "show"
                }
             }
          }
       }
    }

    添加一个bypassed属性,告诉路由器没有匹配到当前的hash路径。并且配置一个notFound的target。

    新建NotFound.view.xml

    <mvc:View
       controllerName="sap.ui.demo.nav.controller.NotFound"
       xmlns="sap.m"
       xmlns:mvc="sap.ui.core.mvc">
       <MessagePage
          title="{i18n>NotFound}"
          text="{i18n>NotFound.text}"
          description="{i18n>NotFound.description}"/>
    </mvc:View>

    新建NotFound.controller.js

    sap.ui.define([
       "sap/ui/core/mvc/Controller"
    ], function (Controller) {
       "use strict";
       return Controller.extend("sap.ui.demo.nav.controller.NotFound", {
          onInit: function () {
          }
       });
    });

    修改i18n.properties

    ...
    NotFound=Not Found
    NotFound.text=Sorry, but the requested resource is not available.
    NotFound.description=Please check the URL and try again.

    使用bypassed属性与他相对应的target来显示,使用sap.m.MessagePage现实路由相关的错误信息。

    Step 4: Add a Back Button to Not Found Page

     修改NotFound.view.xml

    <mvc:View
        controllerName="sap.ui.demo.nav.controller.NotFound"
        xmlns="sap.m"
        xmlns:mvc="sap.ui.core.mvc">
        <MessagePage
            title="{i18n>NotFound}"
            text="{i18n>NotFound.text}"
            description="{i18n>NotFound.description}"
            showNavButton="true"
            navButtonPress="onNavBack"/>
    </mvc:View>

    设置showNavButton,显示后退按钮,当按钮点击时,触发事件onNavBack。

    对于不同的视图,我们可能都需要实现后退功能,所以新建一个basecontroller,使其他控制器都继承他。

    新建BaseController.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.nav.controller.BaseController", {
    
            getRouter : function () {
                return UIComponent.getRouterFor(this);
            },
    
            onNavBack: function () {
                var oHistory, sPreviousHash;
    
                oHistory = History.getInstance();
                sPreviousHash = oHistory.getPreviousHash();
    
                if (sPreviousHash !== undefined) {
                    window.history.go(-1);
                } else {
                    this.getRouter().navTo("appHome", {}, true /*no history*/);
                }
            }
    
        });
    
    });

    创建一个basecontroller,里面的函数可以由其他子类调用。

    onNavBack函数,先检查是否存在以前的hash value,如果有那么重定向到之前的hash value,如果没有,跳转到appHome。

    navTo的第三个参数(“appHome”,{},true /*no history*/);值为true,可以确保替换hash value。

    使用sap.ui.core.UIComponent.getRouterFor(this),可以在整个应用程序中轻松访问组件的路由器。在onNavBack中,它还用于在调用navTo之前获取对路由器的引用。

    在其他cnotroller中继承basecontroller。

    修改NotFound.controller.js

    sap.ui.define([
        "sap/ui/demo/nav/controller/BaseController"
    
    ], function (BaseController) {
        "use strict";
        return BaseController.extend("sap.ui.demo.nav.controller.NotFound", {
            onInit: function () {
            }
        });
    });

    修改App.controller.js

    sap.ui.define([
        "sap/ui/demo/nav/controller/BaseController"
    
    ], function (BaseController) {
        "use strict";
        return BaseController.extend("sap.ui.demo.nav.controller.App", {
            onInit: function () {
            }
        });
    });

    修改Home.controller.js

    sap.ui.define([
        "sap/ui/demo/nav/controller/BaseController"
    
    ], function (BaseController) {
        "use strict";
        return BaseController.extend("sap.ui.demo.nav.controller.Home", {
        });
    });

     

    Step 5: Display a Target Without Changing the Hash

    实现显示目标view而不改变hash的值。

    修改Home.view.xml

    <mvc:View
        controllerName="sap.ui.demo.nav.controller.Home"
        xmlns="sap.m"
        xmlns:mvc="sap.ui.core.mvc">
        <Page title="{i18n>homePageTitle}" class="sapUiResponsiveContentPadding">
            <content>
                <Button id="displayNotFoundBtn" text="{i18n>DisplayNotFound}" press=".onDisplayNotFound" class="sapUiTinyMarginEnd"/>
            </content>
        </Page>
    </mvc:View>

    当按钮被压下时,onDisplayNotFound函数将被执行。

    修改Home.controller.js

    sap.ui.define([
        "sap/ui/demo/nav/controller/BaseController"
    ], function (BaseController) {
        "use strict";
        return BaseController.extend("sap.ui.demo.nav.controller.Home", {
            onDisplayNotFound : function () {
                //display the "notFound" target without changing the hash
                this.getRouter().getTargets().display("notFound");
            }
        });
    });

    在onDisplayNotFound函数中,router将去寻找配置在targets中的notFound,这个页面将由路由来显示,而不需要改变hash value。

    sap.m.routing.targets可以通过该路由的getTargets()来检索。它为将视图放入应用程序提供了一种更方便的方法。

    这里使用了this.getRouter().getTargets()来获得targets对象,其中this.getRouter(),在basecontroller中实现。

    我们还可以用this.getOwnerComponent().getRouter().getTargets() 或者this.getOwnerComponent().getTargets()来获得target对象。

    这时,url不变更,也会显示notFound。但是notFound的后退按钮失效,因为hash value依然是空的,只是显示了页面,没有命中路由。所以,要再次修改Home controller。

    修改Home.controller.js

    sap.ui.define([
        "sap/ui/demo/nav/controller/BaseController"
    ], function (BaseController) {
        "use strict";
        return BaseController.extend("sap.ui.demo.nav.controller.Home", {
            onDisplayNotFound : function () {
                //display the "notFound" target without changing the hash
                this.getRouter().getTargets().display("notFound", {
                    fromTarget : "home"
                });
            }
        });
    });

    设置一个targers的key,用来标志从那个页面迁移过去。

    修改NotFound.controller.js

    sap.ui.define([
        "sap/ui/demo/nav/controller/BaseController"
    ], function (BaseController) {
        "use strict";
    
        return BaseController.extend("sap.ui.demo.nav.controller.NotFound", {
    
            onInit: function () {
                var oRouter, oTarget;
    
                oRouter = this.getRouter();
                oTarget = oRouter.getTarget("notFound");
                oTarget.attachDisplay(function (oEvent) {
                    this._oData = oEvent.getParameter("data");    // store the data
                }, this);
            },
    
            // override the parent's onNavBack (inherited from BaseController)
            onNavBack : function () {
                // in some cases we could display a certain target when the back button is pressed
                if (this._oData && this._oData.fromTarget) {
                    this.getRouter().getTargets().display(this._oData.fromTarget);
                    delete this._oData.fromTarget;
                    return;
                }
    
                // call the parent's onNavBack
                BaseController.prototype.onNavBack.apply(this, arguments);
            }
        });
    });

    当notFound页面显示的时候,在init中注册一个事件监听。

    我们可以从路由器的引用中获取到notFound的引。每个target的配置,都将创建一个可以通过路由器访问的运行时的对象。

    保存页面显示事件的参数到变量 _oData。这个变量中还包括fromTarget信息。

    重写onNavBack函数,判断_oData是否指定了fromTarget,如果指定了,手动显示定义为fromTarget的目标,就像手动调用notFound目标一样。

    这时notFound页面的back按钮就可以点击了。

    修改i18n.properties

    ...
    DisplayNotFound=Display Not Found

    如果希望在不更改hash value的情况下触发导航,需要在代码中实现导航功能。

    Step 6: Navigate to Routes with Hard-Coded Patterns

    导航到员工列表画面,url中有pattern的硬编码

    修改Home.view.xml

    <mvc:View
        controllerName="sap.ui.demo.nav.controller.Home"
        xmlns="sap.m"
        xmlns:mvc="sap.ui.core.mvc">
        <Page title="{i18n>homePageTitle}" class="sapUiResponsiveContentPadding">
            <content>
                <Button id="displayNotFoundBtn" text="{i18n>DisplayNotFound}" press=".onDisplayNotFound" class="sapUiTinyMarginEnd"/>
                <Button id="employeeListBtn" text="{i18n>ShowEmployeeList}" press=".onNavToEmployees" class="sapUiTinyMarginEnd"/>
            </content>
        </Page>

    添加了按钮,当这个按钮,按下的时候,会触发事件onNavToEmployees。

    修改Home.controller.js

    sap.ui.define([
        "sap/ui/demo/nav/controller/BaseController"
    ], function (BaseController) {
        "use strict";
        return BaseController.extend("sap.ui.demo.nav.controller.Home", {
            onDisplayNotFound : function () {
                // display the "notFound" target without changing the hash
                this.getRouter().getTargets().display("notFound", {
                    fromTarget : "home"
                });
            },
            onNavToEmployees : function (){
                this.getRouter().navTo("employeeList");
            }
    
        });
    });

    实装函数onNavToEmployees,执行router实例navTo("employeeList")方法,employeeList就是我们将要导航的页面。

    修改manifest.json

    {
        "_version": "1.12.0",
        "sap.app": {
            ...
        },
        "sap.ui": {
            ...
        },
        "sap.ui5": {
            ...
            "routing": {
                "config": {
                    "routerClass": "sap.m.routing.Router",
                    "viewType": "XML",
                    "viewPath": "sap.ui.demo.nav.view",
                    "controlId": "app",
                    "controlAggregation": "pages",
                    "transition": "slide",
                    "bypassed": {
                        "target": "notFound"
                    }
                },
                "routes": [{
                    "pattern": "",
                    "name": "appHome",
                    "target": "home"
                }, {
                    "pattern": "employees",
                    "name": "employeeList",
                    "target": "employees"
                }],
                "targets": {
                    "home": {
                        "viewId": "home",
                        "viewName": "Home",
                        "viewLevel" : 1
                    },
                    "notFound": {
                        "viewId": "notFound",
                        "viewName": "NotFound",
                        "transition": "show"
                    },
                    "employees": {
                        "viewId": "employeeList",
                        "viewPath": "sap.ui.demo.nav.view.employee",
                        "viewName": "EmployeeList",
                        "viewLevel" : 2
                    }
    
                }
            }
        }
    }

    为了使导航工作,我们必须在manifest.json文件中扩展应用程序的路由配置。我们追加了一个新的pattern employeeList,我们使用这个名字在controller中触发导航。

    employees是pattern的硬编码,表示路由的匹配hash value是浏览器地址栏中的/#/employees。

    target中的employees指向targets中配置的employees,我们为所有与employee相关的视图添加了一个新的名称空间employee,并将路径记录在viewPath中,这将覆盖当前target的config部分中的默认设置。

    我们将要创建的视图必须相应地放在webapp/view/employee文件夹中。

    也可以使用默认的viewPath,但是相应的,必须将viewName更改为employee.EmployeeList,这样才可以正确的读取到view文件。

    viewLevel设置为2,是帮助router决定页面切换时的切换顺序。

    新建EmployeeList.view.xml

    <mvc:View
        controllerName="sap.ui.demo.nav.controller.employee.EmployeeList"
        xmlns="sap.m"
        xmlns:mvc="sap.ui.core.mvc">
        <Page
            id="employeeListPage"
            title="{i18n>EmployeeList}"
            showNavButton="true"
            navButtonPress=".onNavBack"
            class="sapUiResponsiveContentPadding">
            <content>
                <List id="employeeList" headerText="{i18n>ListOfAllEmployees}" items="{/Employees}">
                    <items>
                        <StandardListItem
                            title="{FirstName} {LastName}"
                            iconDensityAware="false"
                            iconInset="false"/>
                    </items>
                </List>
            </content>
        </Page>
    </mvc:View>

    将文件夹命名为业务对象,这样在url中可以很容易的看出view所在的位置。

    比如本例中,可以从URL /#/employee来确定视图位于./employee的某个位置。

    使用sap.m.List绑定数据,加入后退按钮。

    新建EmployeeList.controller.js

    sap.ui.define([
        "sap/ui/demo/nav/controller/BaseController"
    ], function (BaseController) {
        "use strict";
        return BaseController.extend("sap.ui.demo.nav.controller.employee.EmployeeList", {
        });
    });

    创建webapp/controller/employee ,将EmployeeList.controller.js放进去。保持view与controller的文件同步。

    修改i18n.properties

    ...
    ShowEmployeeList=Show Employee List
    EmployeeList=Employee List
    ListOfAllEmployees=List of all employees

    按下按钮后

    Step 7: Navigate to Routes with Mandatory Parameters

    通过参数识别对象。
    从url中读取employee id,通过id从服务器获取员工信息。
    修改manifest.json
    {
        "_version": "1.12.0",
        "sap.app": {
            ...
        },
        "sap.ui": {
            ...
        },
        "sap.ui5": {
            ...
            "routing": {
                "config": {
                    "routerClass": "sap.m.routing.Router",
                    "viewType": "XML",
                    "viewPath": "sap.ui.demo.nav.view",
                    "controlId": "app",
                    "controlAggregation": "pages",
                    "transition": "slide",
                    "bypassed": {
                        "target": "notFound"
                    }
                },
                "routes": [{
                    "pattern": "",
                    "name": "appHome",
                    "target": "home"
                }, {
                    "pattern": "employees",
                    "name": "employeeList",
                    "target": "employees"
                }, {
                    "pattern": "employees/{employeeId}",
                    "name": "employee",
                    "target": "employee"
                }],
                "targets": {
                    "home": {
                        "viewId": "home",
                        "viewName": "Home",
                        "viewLevel" : 1
                    },
                    "notFound": {
                        "viewId": "notFound",
                        "viewName": "NotFound",
                        "transition": "show"
                    },
                    "employees": {
                        "viewId": "employeeList",
                        "viewPath": "sap.ui.demo.nav.view.employee",
                        "viewName": "EmployeeList",
                        "viewLevel" : 2
                    },
                    "employee": {
                        "viewId": "employee",
                        "viewName": "employee.Employee",
                        "viewLevel" : 3
                    }
                }
            }
        }

    在数据模型Employees.json中,每条数据都有一个EmployeeID来标示该条数据,所以我们创建了一个新的路由,并且在该路由中强制使用employeeId来处理员工对象,这里我们使用了employees/{employeeId}作为该路由的pattern。

    大括号 {employeeId} 是一个强制性的参数。在运行的时候,hash中包括employee id,它将与 employees/{employeeId}进行匹配。

    employees/2, employees/7, employees/anInvalidId这种格式的hash会被匹配,但是employees/不会被匹配,因为他没有employee id。使用viewlevel 3创建员工信息,保证页面切换正常显示。

    这里没有使用viewPath,而是使用了viewName:employee.Employee,表示在config配置的viewPath下的,employee.Employee。

    创建Employee.view.xml

    <mvc:View
        controllerName="sap.ui.demo.nav.controller.employee.Employee"
        xmlns="sap.m"
        xmlns:mvc="sap.ui.core.mvc"
        xmlns:f="sap.ui.layout.form"
        busyIndicatorDelay="0">
        <Page
            id="employeePage"
            title="{i18n>EmployeeDetailsOf} {FirstName} {LastName}"
            showNavButton="true"
            navButtonPress=".onNavBack"
            class="sapUiResponsiveContentPadding">
            <content>
                <Panel
                    id="employeePanel"
                    width="auto"
                    class="sapUiResponsiveMargin sapUiNoContentPadding">
                    <headerToolbar>
                        <Toolbar>
                            <Title text="{i18n>EmployeeIDColon} {EmployeeID}" level="H2"/>
                            <ToolbarSpacer />
                        </Toolbar>
                    </headerToolbar>
                    <content>
                        <f:SimpleForm
                            minWidth="1024"
                            editable="false"
                            layout="ResponsiveGridLayout"
                            labelSpanL="3" labelSpanM="3" emptySpanL="4" emptySpanM="4"
                            columnsL="1" columnsM="1">
                            <f:content>
                                <Label text="{i18n>formFirstName}"/>
                                <Text text="{FirstName}"/>
                                <Label text="{i18n>formLastName}"/>
                                <Text text="{LastName}"/>
                                <Label text="{i18n>formAddress}"/>
                                <Text text="{Address}"/>
                                <Label text="{i18n>formCity}"/>
                                <Text text="{City}, {Region}"/>
                                <Label text="{i18n>formPostalCode}"/>
                                <Text text="{PostalCode}"/>
                                <Label text="{i18n>formPhoneHome}"/>
                                <Text text="{HomePhone}"/>
                                <Label text="{i18n>formCountry}"/>
                                <Text text="{Country}"/>
                            </f:content>
                        </f:SimpleForm>
                    </content>
                </Panel>
            </content>
        </Page>
    </mvc:View>

    在webapp/view/employee文件夹中创建Employee.view.xml文件。用来显示员工信息。

    创建Employee.controller.js

    sap.ui.define([
        "sap/ui/demo/nav/controller/BaseController"
    ], function (BaseController) {
        "use strict";
        return BaseController.extend("sap.ui.demo.nav.controller.employee.Employee", {
            onInit: function () {
                var oRouter = this.getRouter();
                oRouter.getRoute("employee").attachMatched(this._onRouteMatched, this);
                // Hint: we don't want to do it this way
                /*
                oRouter.attachRouteMatched(function (oEvent){
                    var sRouteName, oArgs, oView;
                    sRouteName = oEvent.getParameter("name");
                    if (sRouteName === "employee"){
                        this._onRouteMatched(oEvent);
                    }
                }, this);
                */
            },
            _onRouteMatched : function (oEvent) {
                var oArgs, oView;
                oArgs = oEvent.getParameter("arguments");
                oView = this.getView();
    
                oView.bindElement({
                    path : "/Employees(" + oArgs.employeeId + ")",
                    events : {
                        change: this._onBindingChange.bind(this),
                        dataRequested: function (oEvent) {
                            oView.setBusy(true);
                        },
                        dataReceived: function (oEvent) {
                            oView.setBusy(false);
                        }
                    }
                });
            },
            _onBindingChange : function (oEvent) {
                // No data for the binding
                if (!this.getView().getBindingContext()) {
                    this.getRouter().getTargets().display("notFound");
                }
            }
        });
    });
    在webapp/controller/employee文件夹中创建employee .controller.js文件。在这个controller中,我们想检测应该显示哪个员工,以便在视图中显示员工的数据。
    所以我们查询路由employee,并将一个私有函数_onRouteMatched附加到该路由上。
    在事件处理的函数中,可以通过oEvent参数获取arguments,他包含pattern的所以参数。
    因为只是监听匹配的路由,所以可以保证 employeeId 始终作为pattern中的key,否则的话这个路由就不会匹配。
    employeeId 与路由 employee 定义的pattern {employeeId}相关,所以可以通过url获取。
    在函数_onRouteMatched中调用了bindElement()来绑定数据,确保数据可以显示在view中。
    将events参数传递给bindElement(),用来监听dataRequested和dataReceived事件,来将view设置为busy模式。
    我们还添加了一个函数_onBindingChange到更改的事件中,通过查询视图的绑定上下文来检查是否加载了数据。如果无法加载数据,我们将显示notFound目标。
    在controller中不使用oRouter.attachRouteMatched是因为用这种方法,无论匹配任何路由,都会触发该事件,如果我们需要添加额外的判断,以确保当前路由是已匹配的路由。
     
    修改EmployeeList.view.xml
    <mvc:View
        controllerName="sap.ui.demo.nav.controller.employee.EmployeeList"
        xmlns="sap.m"
        xmlns:mvc="sap.ui.core.mvc">
        <Page
            id="employeeListPage"
            title="{i18n>EmployeeList}"
            showNavButton="true"
            navButtonPress=".onNavBack"
            class="sapUiResponsiveContentPadding">
            <content>
                <List id="employeeList" headerText="{i18n>ListOfAllEmployees}" items="{/Employees}">
                    <items>
                        <StandardListItem
                            title="{FirstName} {LastName}"
                            iconDensityAware="false"
                            iconInset="false"
                            type="Navigation"
                            press=".onListItemPressed"/>
                    </items>
                </List>
            </content>
        </Page>
    </mvc:View>

    添加导航,将StandardListItem 的type设置为Navigation,添加一个press事件,当item被点击时,触发onListItemPressed事件。

    修改EmployeeList.controller.js

    sap.ui.define([
        "sap/ui/demo/nav/controller/BaseController"
    ], function (BaseController) {
        "use strict";
        return BaseController.extend("sap.ui.demo.nav.controller.employee.EmployeeList", {
            onListItemPressed : function(oEvent){
                var oItem, oCtx;
                oItem = oEvent.getSource();
                oCtx = oItem.getBindingContext();
                this.getRouter().navTo("employee",{
                    employeeId : oCtx.getProperty("EmployeeID")
                });
            }
        });
    });

    通过当前item的绑定数据,获取EmployeeID,将EmployeeID赋值给employeeId。

    修改i18n.properties

    ...
    formEmployeeDetailsOf=Employee Details of
    formEmployeeIDColon=Employee ID:
    formFirstName=First Name
    formLastName=Last Name
    formAddress=Address
    formCity=City
    formPostalCode=Postal Code
    formPhoneHome=Phone (Home)
    formCountry=Country

     

    Step 8: Navigate with Flip Transition 

    切换页面使用flip。

    修改Employee.view.xml

    <mvc:View
        controllerName="sap.ui.demo.nav.controller.employee.Employee"
        xmlns="sap.m"
        xmlns:mvc="sap.ui.core.mvc"
        xmlns:f="sap.ui.layout.form"
        busyIndicatorDelay="0">
        <Page
            id="employeePage"
            title="{i18n>EmployeeDetailsOf} {FirstName} {LastName}"
            showNavButton="true"
            navButtonPress=".onNavBack"
            class="sapUiResponsiveContentPadding">
            <content>
                <Panel
                    id="employeePanel"
                    width="auto"
                    class="sapUiResponsiveMargin sapUiNoContentPadding">
                    <headerToolbar>
                        <Toolbar>
                            <Title text="{i18n>EmployeeIDColon} {EmployeeID}" level="H2"/>
                            <ToolbarSpacer />
                            <Link text="{i18n>FlipToResume}" tooltip="{i18n>FlipToResume.tooltip}" press=".onShowResume"/>
                        </Toolbar>
                    </headerToolbar>
                    <content>
                        ...
                    </content>
                </Panel>
            </content>
        </Page>
    </mvc:View>

    添加一个link,触发事件,onShowResume。

    修改Employee.controller.js

    sap.ui.define([
        "sap/ui/demo/nav/controller/BaseController"
    ], function (BaseController) {
        "use strict";
        return BaseController.extend("sap.ui.demo.nav.controller.employee.Employee", {
            ...
            _onBindingChange : function (oEvent) {
                // No data for the binding
                if (!this.getView().getBindingContext()) {
                    this.getRouter().getTargets().display("notFound");
                }
            }
                ...
            },
            onShowResume : function (oEvent) {
                var oCtx = this.getView().getElementBinding().getBoundContext();
    
                this.getRouter().navTo("employeeResume", {
                    employeeId : oCtx.getProperty("EmployeeID")
                });
            }
    
        });
    });

    根据绑定内容,获取EmployeeID,作为参数传递给employeeId。并导航到路由emloyeeResume。

    修改manifest.json
    {
        "_version": "1.12.0",
        "sap.app": {
            ...
        },
        "sap.ui": {
            ...
        },
        "sap.ui5": {
            ...
            "routing": {
                "config": {
                    "routerClass": "sap.m.routing.Router",
                    "viewType": "XML",
                    "viewPath": "sap.ui.demo.nav.view",
                    "controlId": "app",
                    "controlAggregation": "pages",
                    "transition": "slide",
                    "bypassed": {
                        "target": "notFound"
                    }
                },
                "routes": [{
                    "pattern": "",
                    "name": "appHome",
                    "target": "home"
                }, {
                    "pattern": "employees",
                    "name": "employeeList",
                    "target": "employees"
                }, {
                    "pattern": "employees/{employeeId}",
                    "name": "employee",
                    "target": "employee"
                }, {
                    "pattern": "employees/{employeeId}/resume",
                    "name": "employeeResume",
                    "target": "employeeResume"
                }],
                "targets": {
                    "home": {
                        "viewId": "home",
                        "viewName": "Home",
                        "viewLevel" : 1
                    },
                    "notFound": {
                        "viewId": "notFound",
                        "viewName": "NotFound",
                        "transition": "show"
                    },
                    "employees": {
                        "viewId": "employees",
                        "viewPath": "sap.ui.demo.nav.view.employee",
                        "viewName": "EmployeeList",
                        "viewLevel" : 2
                    },
                    "employee": {
                        "viewId": "employee",
                        "viewName": "employee.Employee",
                        "viewLevel" : 3
                    },
                    "employeeResume": {
                        "viewId": "resume",
                        "viewName": "employee.Resume",
                        "viewLevel" : 4,
                        "transition": "flip"
                    }
                }
            }
        }
    }

    添加路由emloyeeResume。{employeeId}做为强制参数,/resume做为结尾。

    target employeeResume参照view employee.Resume。层级为4,页面切换模式为filp。transition有四种模式,slide flip show fade。
    添加Resume.view.xml
    <mvc:View
        controllerName="sap.ui.demo.nav.controller.employee.Resume"
        xmlns="sap.m"
        xmlns:mvc="sap.ui.core.mvc">
        <Page
            title="{i18n>ResumeOf} {FirstName} {LastName}"
            id="employeeResumePage"
            showNavButton="true"
            navButtonPress=".onNavBack">
            <content>
                <IconTabBar
                    id="iconTabBar"
                    headerBackgroundDesign="Transparent"
                    class="sapUiResponsiveContentPadding"
                    binding="{Resume}">
                    <items>
                        <IconTabFilter id="infoTab" text="{i18n>tabInfo}" key="Info">
                            <Text text="{Information}"/>
                        </IconTabFilter>
                        <IconTabFilter id="projectsTab" text="{i18n>tabProjects}" key="Projects">
                            <mvc:XMLView viewName="sap.ui.demo.nav.view.employee.ResumeProjects"></mvc:XMLView>
                        </IconTabFilter>
                        <IconTabFilter id="hobbiesTab" text="{i18n>tabHobbies}" key="Hobbies">
                            <Text text="{Hobbies}"/>
                        </IconTabFilter>
                        <IconTabFilter id="notesTab" text="{i18n>tabNotes}" key="Notes">
                            <Text text="{Notes}"/>
                        </IconTabFilter>
                    </items>
                </IconTabBar>
            </content>
        </Page>
    </mvc:View>

    在webapp/view/employee文件夹中创建Resume.view.xml。视图使用IconTabBar显示简历数据。它的绑定属性被设置为{Resume}。

    在IconTabBar中创建了4个tab。其中三个使用了Text控件,还有一个使用了view控件,sapui5将自动加载这个view。

    创建Resume.controller.js

    sap.ui.define([
        "sap/ui/demo/nav/controller/BaseController"
    ], function (BaseController) {
        "use strict";
        return BaseController.extend("sap.ui.demo.nav.controller.employee.Resume", {
            onInit: function () {
                var oRouter = this.getRouter();
                oRouter.getRoute("employeeResume").attachMatched(this._onRouteMatched, this);
            },
            _onRouteMatched : function (oEvent) {
                var oArgs, oView;
                oArgs = oEvent.getParameter("arguments");
                oView = this.getView();
                oView.bindElement({
                    path : "/Employees(" + oArgs.employeeId + ")",
                    events : {
                        change: this._onBindingChange.bind(this),
                        dataRequested: function (oEvent) {
                            oView.setBusy(true);
                        },
                        dataReceived: function (oEvent) {
                            oView.setBusy(false);
                        }
                    }
                });
            },
            _onBindingChange : function (oEvent) {
                // No data for the binding
                if (!this.getView().getBindingContext()) {
                    this.getRouter().getTargets().display("notFound");
                }
            }
        });
    });

    创建ResumeProjects.view.xml 

    <mvc:View xmlns="sap.m" xmlns:mvc="sap.ui.core.mvc">
        <Text text="{Projects}"/>
    </mvc:View>

    这个文件不需要controller,是因为我们只需要他现实数据。

    修改i18n.properties
    ResumeOf=Resume of
    tabInfo=Info
    tabProjects=Projects
    tabHobbies=Hobbies
    tabNotes=Notes
    FlipToResume=Flip to Resume
    FlipToResume.tooltip=See the resume of this employee

    Step 9: Allow Bookmarkable Tabs with Optional Query Parameters

    添加可选参数,导航到相应的tag。

    在url中使用可选的参数,通过deep linking到对应的tag页。

    修改manifect.json

    {
        "_version": "1.12.0",
        "sap.app": {
            ...
        },
        "sap.ui": {
            ...
        },
        "sap.ui5": {
            ...
            "routing": {
                "config": {
                    "routerClass": "sap.m.routing.Router",
                    "viewType": "XML",
                    "viewPath": "sap.ui.demo.nav.view",
                    "controlId": "app",
                    "controlAggregation": "pages",
                    "transition": "slide",
                    "bypassed": {
                        "target": "notFound"
                    }
                },
                "routes": [{
                    "pattern": "",
                    "name": "appHome",
                    "target": "home"
                }, {
                    "pattern": "employees",
                    "name": "employeeList",
                    "target": "employees"
                }, {
                    "pattern": "employees/{employeeId}",
                    "name": "employee",
                    "target": "employee"
                }, {
                    "pattern": "employees/{employeeId}/resume:?query:",
                    "name": "employeeResume",
                    "target": "employeeResume"
                }],
                "targets": {
                    ...
                }
            }
        }
    }

    现在只能导航到resume的页面,这时页面将始终固定在第一个tag上。为了定位tag,这里添加一个query参数到pattern中。

    新的部分:?query:允许传递带有任何参数的查询,例如 /#/employees/3/resume?tab=Projects or /#/employees/3/resume?tab=Projects&action=edit。

    :?query:用冒号开始与结束,这表示他是可选的。如果需要他是强制参数,那么就用 {?query} ,大括号的内容,都被认为是强制的。

    修改Resume.view.xml

    <mvc:View
        controllerName="sap.ui.demo.nav.controller.employee.Resume"
        xmlns="sap.m"
        xmlns:mvc="sap.ui.core.mvc">
        <Page
            title="{i18n>ResumeOf} {FirstName} {LastName}"
            id="employeeResumePage"
            showNavButton="true"
            navButtonPress=".onNavBack">
            <content>
                <IconTabBar
                    id="iconTabBar"
                    headerBackgroundDesign="Transparent"
                    class="sapUiResponsiveContentPadding"
                    binding="{Resume}"
                    select=".onTabSelect"
                    selectedKey="{view>/selectedTabKey}">
                    <items>
                        <IconTabFilter id="infoTab" text="{i18n>tabInfo}" key="Info">
                            <Text text="{Information}"/>
                        </IconTabFilter>
                        <IconTabFilter id="projectsTab" text="{i18n>tabProjects}" key="Projects">
                            <mvc:XMLView viewName="sap.ui.demo.nav.view.employee.ResumeProjects"></mvc:XMLView>
                        </IconTabFilter>
                        <IconTabFilter id="hobbiesTab" text="{i18n>tabHobbies}" key="Hobbies">
                            <Text text="{Hobbies}"/>
                        </IconTabFilter>
                        <IconTabFilter id="notesTab" text="{i18n>tabNotes}" key="Notes">
                            <Text text="{Notes}"/>
                        </IconTabFilter>
                    </items>
                </IconTabBar>
            </content>
        </Page>
    </mvc:View>

    select=".onTabSelect"用来监听IconTabBar的select事件。selectedKey绑定到视图模型。

    修改Resume.controller.js

    sap.ui.define([
        "sap/ui/demo/nav/controller/BaseController",
        "sap/ui/model/json/JSONModel"
    
    ], function (BaseController, JSONModel) {
        "use strict";
        var _aValidTabKeys = ["Info", "Projects", "Hobbies", "Notes"];
        return BaseController.extend("sap.ui.demo.nav.controller.employee.Resume", {
            onInit: function () {
                var oRouter = this.getRouter();
                this.getView().setModel(new JSONModel(), "view");
    
                oRouter.getRoute("employeeResume").attachMatched(this._onRouteMatched, this);
            },
            _onRouteMatched : function (oEvent) {
                var oArgs, oView, oQuery;
                oArgs = oEvent.getParameter("arguments");
                oView = this.getView();
                oView.bindElement({
                    path : "/Employees(" + oArgs.employeeId + ")",
                    events : {
                        change: this._onBindingChange.bind(this),
                        dataRequested: function (oEvent) {
                            oView.setBusy(true);
                        },
                        dataReceived: function (oEvent) {
                            oView.setBusy(false);
                        }
                    }
                });
                oQuery = oArgs["?query"];
                if (oQuery && _aValidTabKeys.indexOf(oQuery.tab) > -1){
                    oView.getModel("view").setProperty("/selectedTabKey", oQuery.tab);
                } else {
                    // the default query param should be visible at all time
                    this.getRouter().navTo("employeeResume", {
                        employeeId : oArgs.employeeId,
                        query: {
                            tab : _aValidTabKeys[0]
                        }
                    },true /*no history*/);
                }
    
            },
            _onBindingChange : function (oEvent) {
                // No data for the binding
                if (!this.getView().getBindingContext()) {
                    this.getRouter().getTargets().display("notFound");
                }
            },
            onTabSelect : function (oEvent){
                var oCtx = this.getView().getBindingContext();
                this.getRouter().navTo("employeeResume", {
                    employeeId : oCtx.getProperty("EmployeeID"),
                    query: {
                        tab : oEvent.getParameter("selectedKey")
                    }
                }, true /*without history*/);
            }
    
        });
    });

    当tab被手动改变时,select事件会被触发。他将取得employeeID,与selectedKey,导航到employeeResume来更新url。第三个参数为true,表示当前进行tag切换的操作,不会被作为历史记录添加到浏览器中,这样当我们点返回按钮的时候就会跳到之前的页面,而不是跳回之前的tag。

    onTabSelect事件中的this.getRouter().navTo()执行的时候,也会触发_onRouteMatched(),是因为在init页面的时候,路由已经被绑定了该监听方法。

    在onInit事件中,添加一个jsonmodel,做为视图模型。数组_aValidTabKeys用来保证选择的tag,是在范围之内的。这些key就是IconTabFilters控件中的key。

    _onRouteMatched中的oQuery,用来存储url中query的值。如果query传值了,并且这个值在_aValidTabKeys中,那么就将值放入IconTabBar的selectedKey中。否则就设置为第一个tag。

     

  • 相关阅读:
    python 基础类与对象函数实例
    python 练习之炮台
    python练习之银行模拟系统
    python练习之析构函数(进阶)
    python练习之__str__
    「JLOI2011」飞行路线
    「国家集训队」Crash的数字表格
    斐波那契公约数的相关证明
    「JSOI2010」满汉全席
    动态规划
  • 原文地址:https://www.cnblogs.com/suoluo119/p/11533359.html
Copyright © 2020-2023  润新知