SAPUI5主详细信息模板应用程序 - 多个实体集,绑定数据

时间:2017-11-17 12:35:41

标签: odata sapui5 abap

我在Web IDE中创建了一个Master Detail模板应用程序。 (见图)

RSA Fiori App

我有一个包含许多EntitySets的OData服务(ZSV_SURVEY_SRV)。

Master(左侧)调用我的EntitySet SurveySet 并加载找到的所有调查。

详细信息(右侧)调用我的EntitySet QuestionSet ,我刚刚获得它,所以它加载了所有找到的问题。我真正需要做的是只返回主要部分中选择的调查特有的问题。

这是我第一次使用Master Detail应用程序模板,而且我有点迷失了从一个与另一个EntitySet中的数据相关的EntitySet中提取数据。

尝试了不同的绑定变体,但是现在只需将它设置为从QuestionSet中读取所有内容。

明细视图:

<mvc:View xmlns="sap.m" xmlns:core="sap.ui.core" xmlns:mvc="sap.ui.core.mvc" xmlns:l="sap.ui.layout" xmlns:f="sap.ui.layout.form"
xmlns:semantic="sap.m.semantic" xmlns:footerbar="sap.ushell.ui.footerbar" controllerName="managesurveys.controller.Detail">
<semantic:DetailPage id="page" navButtonPress="onNavBack" showNavButton="{device>/system/phone}" title="{Name}" busy="{detailView>/busy}"
    busyIndicatorDelay="{detailView>/delay}">
    <semantic:content>
        <!-- Start of Survey Details Form -->
        <f:Form editable="true" class="sapUiSmallMarginTop">
            <f:formContainers>
                <!-- Survey Details -->
                <f:FormContainer>
                    <f:formElements>
                        <!-- Survey Name -->
                        <f:FormElement label="{i18n>surveyName}">
                            <f:fields>
                                <Input value="{Name}" width="100%" id="__inputSurveyName" liveChange="validateForm"/>
                            </f:fields>
                        </f:FormElement>
                        <!-- Description -->
                        <f:FormElement label="{i18n>description}">
                            <f:fields>
                                <TextArea value="{SurveyDesc}" id="__areaSurveyDescription"/>
                            </f:fields>
                        </f:FormElement>
                    </f:formElements>
                </f:FormContainer>
                <f:FormContainer>
                    <f:formElements>
                        <f:FormElement label="{i18n>reporting}">
                            <f:fields>
                                <RadioButtonGroup width="100%" selectedIndex="-1" id="__group0" columns="3">
                                    <buttons>
                                        <RadioButton selected="true" text="{i18n>onSubmission}" id="__reportingOnSubmission" select="onReportingSelected"/>
                                        <RadioButton text="{i18n>onDate}" id="__reportingOnDate" select="onReportingDateSelected"/>
                                        <RadioButton text="{i18n>none}" id="__reportingNo" select="onNoReportingSelected"/>
                                    </buttons>
                                </RadioButtonGroup>
                            </f:fields>
                        </f:FormElement>
                        <f:FormElement label="" id="__reportDateElement" visible="false">
                            <f:fields>
                                <DatePicker id="__reportDate" placeholder="{i18n>enterDate}" change="onReportingDateChange"/>
                            </f:fields>
                        </f:FormElement>
                    </f:formElements>
                </f:FormContainer>
            </f:formContainers>
            <f:layout>
                <f:ResponsiveGridLayout/>
            </f:layout>
        </f:Form>
        <!-- Categories -->
        <!--<IconTabBar items="{/CategorySet}" id="iconTabBar" enableTabReordering="true" class="sapUiResponsiveContentPadding" expandable="false">-->
            <!--<items>-->
                <!--<IconTabFilter text="{CategoryDesc}">-->
                    <List  id="list" mode="SingleSelectMaster" delete="handleDelete" items="{path:'/QuestionSet', templateShareable:true}"
                        includeItemInSelection="true" selectionChange="onQuestionSelect" headerText="{i18n>questions}">
                        <CustomListItem>
                            <l:Grid>
                                <Text text="ID ({SurveyId}) - Question ({QuestionDesc})"
                                    class="sapUiTinyMarginTop sapUiLargeMarginBegin">
                                    <layoutData>
                                        <l:GridData span="XL9 L9 M9 S9"/>
                                    </layoutData>
                                </Text>
                                <Button icon="sap-icon://navigation-up-arrow">
                                    <layoutData>
                                        <l:GridData span="XL1 L1 M1 S1"/>
                                    </layoutData>
                                </Button>
                                <Button icon="sap-icon://navigation-down-arrow">
                                    <layoutData>
                                        <l:GridData span="XL1 L1 M1 S1"/>
                                    </layoutData>
                                </Button>
                                <Button icon="sap-icon://delete" type="Reject">
                                    <layoutData>
                                        <l:GridData span="XL1 L1 M1 S1"/>
                                    </layoutData>
                                </Button>
                            </l:Grid>
                        </CustomListItem>
                    </List>
                <!--</IconTabFilter>-->
            <!--</items>-->
        <!--</IconTabBar>-->
    </semantic:content>
    <!-- Footer -->
    <semantic:positiveAction>
        <semantic:PositiveAction text="{i18n>btnSaveSurvey}" id="btnSurveySave" press="onSaveSurvey" enabled="false"/>
    </semantic:positiveAction>
</semantic:DetailPage>

详情控制器:

sap.ui.define([
"managesurveys/controller/BaseController",
"sap/ui/model/json/JSONModel",
"managesurveys/model/formatter",
"sap/m/MessageBox",
"sap/m/MessageToast",
"managesurveys/libs/underscore"

],function(BaseController,JSONModel,formatter,MessageBox,MessageToast,UnderScoreJS){     “使用严格”;

return BaseController.extend("managesurveys.controller.Detail", {

    formatter: formatter,

    /* =========================================================== */
    /* lifecycle methods                                           */
    /* =========================================================== */

    onInit: function() {
        // Model used to manipulate control states. The chosen values make sure,
        // detail page is busy indication immediately so there is no break in
        // between the busy indication for loading the view's meta data
        var oViewModel = new JSONModel({
            busy: false,
            delay: 0,
            questions: [{
                order: 1,
                title: "",
                criteria: "",
                category: "",
                competency: "",
                showScore: false,
                addImage: false,
                questionType: 0,
                minimumCommentsLength: 10,
                additionalQuestionTitle: "1",
                additionalQuestionDesc: "2",
                additionalQuestionType: 1,
                numberOfAnswers: 3,
                answers: [{
                    sequence: 1,
                    label: "Yes",
                    points: 2,
                    imageFlag: false,
                    image: "/images/happy-1.svg",
                    mandatoryComments: false,
                    additionalQuestion: false
                }, {
                    sequence: 2,
                    label: "No",
                    points: 0,
                    imageFlag: false,
                    image: "/images/sceptic.svg",
                    mandatoryComments: false,
                    additionalQuestion: false
                }, {
                    sequence: 3,
                    label: "N/A",
                    points: -2,
                    imageFlag: false,
                    image: "/images/angry-2.svg",
                    mandatoryComments: false,
                    additionalQuestion: false
                }]
            }]
        });


        this.getRouter().getRoute("object").attachPatternMatched(this._onObjectMatched, this);

        this.setModel(oViewModel, "detailView");

        this.getOwnerComponent().getModel().metadataLoaded().then(this._onMetadataLoaded.bind(this));
    },

    /* =========================================================== */
    /* begin: internal methods                                     */
    /* =========================================================== */

    /**
     * Binds the view to the object path and expands the aggregated line items.
     * @function
     * @param {sap.ui.base.Event} oEvent pattern match event in route 'object'
     * @private
     */
    _onObjectMatched: function(oEvent) {
        var sObjectId = oEvent.getParameter("arguments").objectId;
        this.getModel().metadataLoaded().then(function() {
            var sObjectPath = this.getModel().createKey("SurveySet", {
                SurveyId: sObjectId
            });
            this._bindView("/" + sObjectPath);
        }.bind(this));
    },

    /**
     * Binds the view to the object path. Makes sure that detail view displays
     * a busy indicator while data for the corresponding element binding is loaded.
     * @function
     * @param {string} sObjectPath path to the object to be bound to the view.
     * @private
     */
    _bindView: function(sObjectPath) {
        // Set busy indicator during view binding
        var oViewModel = this.getModel("detailView");

        // If the view was not bound yet its not busy, only if the binding requests data it is set to busy again
        oViewModel.setProperty("/busy", false);

        this.getView().bindElement({
            path: sObjectPath,
            events: {
                change: this._onBindingChange.bind(this),
                dataRequested: function() {
                    oViewModel.setProperty("/busy", true);
                },
                dataReceived: function() {
                    oViewModel.setProperty("/busy", false);
                }
            }
        });
    },

    _onBindingChange: function() {
        var oView = this.getView(),
            oElementBinding = oView.getElementBinding();

        // No data for the binding
        if (!oElementBinding.getBoundContext()) {
            this.getRouter().getTargets().display("detailObjectNotFound");
            // if object could not be found, the selection in the master list
            // does not make sense anymore.
            this.getOwnerComponent().oListSelector.clearMasterListSelection();
            return;
        }

        var sPath = oElementBinding.getPath(),
            oResourceBundle = this.getResourceBundle(),
            oObject = oView.getModel().getObject(sPath),
            sObjectId = oObject.SurveyId,
            sObjectName = oObject.SurveyDesc,
            oViewModel = this.getModel("detailView");

        this.getOwnerComponent().oListSelector.selectAListItem(sPath);

        oViewModel.setProperty("/saveAsTileTitle", oResourceBundle.getText("shareSaveTileAppTitle", [sObjectName]));
        oViewModel.setProperty("/shareOnJamTitle", sObjectName);
        oViewModel.setProperty("/shareSendEmailSubject",
            oResourceBundle.getText("shareSendEmailObjectSubject", [sObjectId]));
        oViewModel.setProperty("/shareSendEmailMessage",
            oResourceBundle.getText("shareSendEmailObjectMessage", [sObjectName, sObjectId, location.href]));
    },

    _onMetadataLoaded: function() {
        // Store original busy indicator delay for the detail view
        var iOriginalViewBusyDelay = this.getView().getBusyIndicatorDelay(),
            oViewModel = this.getModel("detailView");

        // Make sure busy indicator is displayed immediately when
        // detail view is displayed for the first time
        oViewModel.setProperty("/delay", 0);

        // Binding the view will set it to not busy - so the view is always busy if it is not bound
        oViewModel.setProperty("/busy", true);
        // Restore original busy indicator delay for the detail view
        oViewModel.setProperty("/delay", iOriginalViewBusyDelay);
    },

    // Question radio button selected, ready for delete question 
    onQuestionSelect: function(oEvent) {
        console.log("Question Selected");
        this.getView().byId("btnSurveySave").setEnabled(true);

        var oRouter = sap.ui.core.UIComponent.getRouterFor(this);
        oRouter.navTo("questiondetails");


        //var oContext = oEvent.getParameters().listItem.getBindingContext(this.MODEL_SITE);
        //var oData = oContext.getModel().getProperty(oContext.getPath());
        //this.getRouter().navTo("QuestionDetails", {QuestionId: oData.QuestionId});
    },

    // Delete Question Message Box  
    onDeleteQuestion: function() {
        var that = this;
        var oViewModel = this.getModel("masterView");
        MessageBox.show("Are you sure you want to delete this Question?", {
            icon: MessageBox.Icon.ERROR,
            title: "Delete Question",
            actions: [MessageBox.Action.YES, MessageBox.Action.NO],
            onClose: function(oAction) {
                if (oAction === MessageBox.Action.YES) {
                    that.getModel().remove("/SurveySet", {
                        SurveyId: oViewModel.SurveyId
                    }, {
                        success: function() {
                            MessageToast.show("Survey successfully deleted", {
                                duration: 2000
                            });
                        },
                        error: function() {
                            MessageToast.show("Error deleting Survey", {
                                duration: 2000
                            });
                        }
                    });
                }
            }
        });
    },

    // Save Survey
    onSaveSurvey: function() {
        MessageToast.show("Survey successfully saved", {
            duration: 2000
        });
    }

});

});

1 个答案:

答案 0 :(得分:0)

您的问题非常常见,并且只与主/明细面应用程序无关。

实际上,所需的解决方案就像修复UI5代码一样简单,但在oData模型中修复了混合(因此,您必须在SEGW事务中调整SAP Gateway项目)+服务实现+ UI5代码。

oData part

您需要在实体类型之间创建关联+导航属性。

假设您的实体类型名称是Survey and Question。

  1. 在Survey和Question实体类型之间创建关联。由于调查可能在您的模型中存在多个问题,因此将基数设置为1非常重要:n
  2. 在SurveySet和QuestionSet实体集之间创建关联集
  3. 在Survey实体类型中创建一个导航属性,该属性与您创建的关联链接。我们假设您的导航属性称为“ToQuestions”
  4. 这可以修复你的odata模型

    您的oData调用类似于:

    Get all surveys
    GET /sap/opu/odata/sap/ZSV_SURVEY_SRV/SurveySet
    
    Get a specific survey
    GET /sap/opu/odata/sap/ZSV_SURVEY_SRV/SurveySet(1)
    
    Get all questions from a survey
    GET /sap/opu/odata/sap/ZSV_SURVEY_SRV/SurveySet(1)/ToQuestions
    

    ABAP部分

    假设您已使用网关服务生成来实现项目,则必须在功能模块中添加一个必需的导入参数来获取您的问题(使用QuestionSet的GetEntitySet操作映射的参数)。此参数将表示调查的ID。您还必须使用imporing参数映射SurveyId属性。因此,您在SurveyId Propety的映射中将有两行。一行将有一个指向右侧的箭头(对于导入参数),另一行将有一个指向左侧的箭头(对于导出的内部表的调查ID列)

    假设您没有使用服务生成但服务实现(当您在DPC_EXT类中手动开发ABAP代码时),您将需要使用以下方法调用来读取调查ID:

    io_tech_request_context->get_converted_keys(
      IMPORTING
        es_key_values = ls_converted_keys ).
    

    UI5部分

    您还必须在UI5代码中进行小修复。

    除了在列表聚合绑定中使用第二个实体集之外,就像这样......

    <List  items="{path:'/QuestionSet', templateShareable:true}" >
    

    您必须使用导航属性的完全名称

    <List  items="{path:'/ToQuestions' }" >
    

    或只是

    <List  items="{/ToQuestions}" >
    

    我打赌你的场景中不需要templateShareable。

    因此,详细信息页面将与'/ SurveySet(1)'绑定,因为您已经调用了.bindElement方法

    this._bindView("/" + sObjectPath);
    

    因此,相对于此绑定,您的问题表将被绑定到/ ToQuestions导航属性。