【问题标题】:SAPUI5 Master Detail Template App - Multiple EntitySets, binding dataSAPUI5 Master Detail Template App - 多个EntitySets,绑定数据
【发布时间】:2017-11-17 12:35:40
【问题描述】:

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

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

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

Detail(右侧)调用我的 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
        });
    }

});

});

【问题讨论】:

  • 如果您不熟悉整个 Master Detail 概念,只需在网络上搜索教程和示例即可。有很多,这个网站不是教程。例如。本教程:blogs.sap.com/2016/11/28/… 另外我认为您没有多个 odata 服务。你只有两个实体集?您的具体问题可能是由错误的数据绑定引起的。如果您发布截取详细视图的代码,人们也可以更好地帮助您
  • 嗨,Thomas,是的,我的意思是两个实体集,我急于在午餐前发布此内容:) 我也会用一些示例代码对其进行更新。
  • 主屏幕和详细屏幕应该只有一个模型:SurveySet?$expand=QuestionSet。正如 Thomas 所说,网上有很多教程可以做到这一点。
  • $expand 会从所有调查中获取所有问题,这代表不必要的超载

标签: odata sapui5 abap


【解决方案1】:

您的问题很常见,它不仅仅与主/从应用程序相关。

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

o数据部分

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

假设您的实体类型名称是调查和问题。

  1. 在调查和问题实体类型之间创建关联。由于调查可能在您的模型中有多个问题,因此将基数设置为 1:n 非常重要
  2. 在 SurveySet 和 QuestionSet 实体集之间创建关联集
  3. 在与您创建的关联链接的调查实体类型中创建导航属性。假设您的导航属性名为“ToQuestions”

这会修复您的 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。您还必须使用导入参数映射 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 导航属性。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-07-09
    • 2016-04-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-09-01
    • 1970-01-01
    相关资源
    最近更新 更多