SAPUI5 (24) - 增删改查之查询数据
阅读原文时间:2021年04月20日阅读:1

CRUD是应用程序的核心。openui5是一个前端的UI库,CRUD是通过oData的服务来完成。openui5提交基于http协议的请求给服务器,其它交给服务器端处理。oData协议是微软公司发起,比web service更轻量级的通讯协议。SAP的Neweaver Gateway 就是SAP以OData形式暴露后端业务数据的一个组件。SAPUI5提供的方便的数据绑定功能,将前端展示组件绑定到后端暴露的OData服务上。

使用mock server实现数据查询

目前,我们先使用mock server来模拟服务器端的功能。本次将之前的master-detail程序修改为使用GET请求获取数据并展示。在前一篇起始框架代码的基础上,做如下变更:

  1. 创建master view和master controller
  2. 创建detail view和detail controller
  3. 修改manifest.json, 将空字符串的target设为master

项目文件结构:

[外链图片转存失败(img-3uKoHu0C-1564974244229)(https://raw.githubusercontent.com/stonewm/my_blogs/master/sap_openui5/pics/24/project_files.png)]

创建master view和master controller

Master.view.xml:

<core:View xmlns:core="sap.ui.core" 
           xmlns:mvc="sap.ui.core.mvc" 
           xmlns="sap.m"
            controllerName="stonewang.sapui5.demo.controller.Master" 
            xmlns:html="http://www.w3.org/1999/xhtml">

    <Page id="page" title="{i18n>masterViewTitle}">
        <content>
            <Table id="table" width="auto"
                class="sapUiResponsiveMargin"
                items="{/}"
                noDataText="{i18n>tableNoDataText}"
                growing="true"
                growingScrollToLoad="true">

                <headerToolbar>
                    <Toolbar>
                        <Title id="tableHeader" text="Suppliers List"/>
                        <ToolbarSpacer />
                    </Toolbar>
                </headerToolbar>

                <columns>
                    <Column id="nameColumn"><header>
                        <Text text="{i18n>tableIDColumnTitle}" id="IDColumnTitle"/></header>
                    </Column>
                    <Column id="unitNumberColumn"><header>
                        <Text text="{i18n>tableNameColumnTitle}" id="nameColumnTitle"/></header>
                    </Column>
                </columns>

                <items>
                    <ColumnListItem
                        type="Navigation" press="onItemPress">
                        <cells>
                            <ObjectIdentifier text="{id}"/>
                            <ObjectIdentifier text="{Name}"/>
                        </cells>
                    </ColumnListItem>
                </items>
            </Table>

        </content>  
    </Page>

</core:View>

重点说明:

  • 因为Component.js文件中:

    var oAppModel = new sap.ui.model.json.JSONModel("/Suppliers");

直接将路径设为/Suppliers,所以上面代码中,master viewsap.m.Tableitems属性设置为{/},是相对于/Suppliers的。这将导致后面我们在表达model中数据的路径时,和之前不同。

Master.controller.js:

sap.ui.define([
        "sap/ui/core/mvc/Controller",
        "sap/ui/core/UIComponent"
    ],         

    function(Controller, UIComponent){
       "use strict";

        return Controller.extend("stonewang.sapui5.demo.controller.Master", {

            onItemPress: function(oEvent){
                var oRouter = UIComponent.getRouterFor(this);
                var oItem = oEvent.getSource();

                var sPath = oItem.getBindingContext().getPath();
                oRouter.navTo("detail", {
                     supplierPath: encodeURIComponent(sPath)
                }, true);
             }
         });

    }
);

和之前的代码相比,没有变化。

创建detail view和detail controller

Detail.view.xml:

<core:View xmlns:core="sap.ui.core" 
           xmlns:mvc="sap.ui.core.mvc" 
           xmlns="sap.m"
            controllerName="stonewang.sapui5.demo.controller.Detail" 
            xmlns:html="http://www.w3.org/1999/xhtml">

    <Page id="page" showNavButton="true" navButtonPress="onNavPress"
          title="{i18n>detailTitle}">

        <content>
            <ObjectHeader title="{Name}" number="ID: {id}">
                <ObjectAttribute text="{Address/Street}, {Address/City}"/>
            </ObjectHeader>
        </content>

    </Page>

</core:View>

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("stonewang.sapui5.demo.controller.Detail", {

            // add functions here
            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 = oEvent.getParameter("arguments").supplierPath;
                console.log(decodeURIComponent(sPath));
                this.getView().bindElement({path: decodeURIComponent(sPath)});
            }
         });    
    }
);

修改manifest.json, 将空字符串的target设为master

修改target,并增加master和detail所映射的view:

"sap.ui5": {
        ...

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

使用view model

现在我们要在detail view中增加两个按钮,用于直接在页面中导航到上一条和下一条数据。界面如下:

[外链图片转存失败(img-ME4fxNVs-1564974244230)(https://raw.githubusercontent.com/stonewm/my_blogs/master/sap_openui5/pics/24/detailview_add_buttons.png)]

同时,这两个按钮根据当前所在行,enabled属性动态变化,比如当处在第一行,向上就为灰色。view model适合这种动态设定UI元素的状态。方法:

1) 实例化view model

Detail.controller.js文件的onInit事件处理器中实例化view model:

// 创建一个model view, 包含两个button是否enabled的布尔值
var oViewModel = new sap.ui.model.json.JSONModel({
    canGoPrev: false,
    canGoNext: false
});

this.getView().setModel(oViewModel, "viewModel");

2) Detail view中增加按钮

Detail.view.xml文件中增加两个按钮,设置enabled属性:

<subHeader>
    <Toolbar>
        <ToolbarSpacer />
        <Button icon="sap-icon://slim-arrow-up" 
                        press="onPageUp" 
                        enabled="{viewModel>/canGoPrev}" />
        <Button icon="sap-icon://slim-arrow-down" 
                        press="onPageDown" 
                        enabled="{viewModel>/canGoNext}" />
    </Toolbar>      
</subHeader>

3) 在detail controller中增加更新状态的代码

Detail.controller.js文件中的_onObjectMatched函数中,定义sObjectID:

this.sObjectID = sPath.substr(sPath.lastIndexOf("/")+1);

因为router的导航是基于路径的,这种方法可以获取最后一个/之后的数字,可以用于知道当前数据所在位置。
比如当处于第一条数据,sObjectID=0,当处于第二条数据,sObjectID=1。

然后再添加一个方法_updateViewModel

_updateViewModel: function(oEvent){
    var oModel = this.getView().getModel();
    var that = this;
    var oViewModel = that.getView().getModel("viewModel");  

    var nextObjId = parseInt(that.sObjectID) + 1;
    var prevObjId = parseInt(that.sObjectID) - 1;                   

    var bNextEnable = !!oModel.getProperty("/" + nextObjId);
    var bPrevEnable = !!oModel.getProperty("/" + prevObjId);

    oViewModel.setProperty("/canGoNext", bNextEnable);
    oViewModel.setProperty("/canGoPrev", bPrevEnable);
    }
 });

代码说明:

  • sObjectId转换成number,加1,使用oModel.getProperty("/" + nextObjId)看看是否存在下一条记录。如果不存在,则表达式为undefinedvar bNextEnable = !!oModel.getProperty("/" + nextObjId);将表达式转换成与之对应的boolean类型。比如表达式为undefined,则!exp为true,!!exp为false

  • 根据这两个值,利用JSONModel.setProperty设置view model的canGoNextcanGoPrev

  • _updateViewModel函数完成,在_onObjectMatched添加调用的语句,_onObjectMatched函数的完整代码如下:

    _onObjectMatched: function (oEvent) {
    // 数据绑定
    var sPath
    = decodeURIComponent(oEvent.getParameter("arguments").supplierPath);
    this.getView().bindElement({path: sPath});

    this.sObjectID = sPath.substr(sPath.lastIndexOf("/")+1);
    this._updateViewModel();

    }

4) 添加onPageUp和onPageDown处理代码

onPageUp: function(oEvent){                
    var sId = parseInt(this.sObjectID);
    sId = sId - 1;

    var oRouter = UIComponent.getRouterFor(this);
    var sNewRoute = encodeURIComponent("/"+sId);
    oRouter.navTo("detail", {supplierPath: sNewRoute});
},

onPageDown: function(oEvent){
    var sId = parseInt(this.sObjectID);
    sId = sId + 1;

    var oRouter = UIComponent.getRouterFor(this);               
    var sNewRoute = encodeURIComponent("/"+sId);
    oRouter.navTo("detail", {supplierPath: sNewRoute});
},

切换到其它数据,也是利用routernavTo方法。

源码:

24_zui5_crud_retrieve_v1

24_zui5_crud_retrieve_v2